2 Copyright (C) 2002, 2003, 2004, 2005, 2006, Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
41 import java.awt.Color;
42 import java.awt.Component;
43 import java.awt.ComponentOrientation;
44 import java.awt.Cursor;
45 import java.awt.Dimension;
47 import java.awt.FontMetrics;
48 import java.awt.Point;
49 import java.awt.Rectangle;
50 import java.awt.event.FocusListener;
51 import java.beans.PropertyChangeEvent;
52 import java.beans.PropertyChangeListener;
53 import java.util.Locale;
54 import java.util.Vector;
56 import javax.accessibility.Accessible;
57 import javax.accessibility.AccessibleComponent;
58 import javax.accessibility.AccessibleContext;
59 import javax.accessibility.AccessibleRole;
60 import javax.accessibility.AccessibleSelection;
61 import javax.accessibility.AccessibleState;
62 import javax.accessibility.AccessibleStateSet;
63 import javax.swing.event.ListDataEvent;
64 import javax.swing.event.ListDataListener;
65 import javax.swing.event.ListSelectionEvent;
66 import javax.swing.event.ListSelectionListener;
67 import javax.swing.plaf.ListUI;
68 import javax.swing.text.Position;
71 * <p>This class is a facade over three separate objects: {@link
72 * javax.swing.ListModel}, {@link javax.swing.ListSelectionModel} and
73 * {@link javax.swing.plaf.ListUI}. The facade represents a unified "list"
74 * concept, with independently replacable (possibly client-provided) models
75 * for its contents and its current selection. In addition, each element in
76 * the list is rendered via a strategy class {@link
77 * javax.swing.ListCellRenderer}.</p>
79 * <p>Lists have many properties, some of which are stored in this class
80 * while others are delegated to the list's model or selection. The
81 * following properties are available:</p>
84 * <tr><th>Property </th><th>Stored in</th><th>Bound?</th></tr>
85 * <tr><td>accessibleContext </td><td>list </td><td>no </td></tr>
86 * <tr><td>anchorSelectionIndex </td><td>selection</td><td>no </td></tr>
87 * <tr><td>cellRenderer </td><td>list </td><td>yes </td></tr>
88 * <tr><td>dragEnabled </td><td>list </td><td>no </td></tr>
89 * <tr><td>firstVisibleIndex </td><td>list </td><td>no </td></tr>
90 * <tr><td>fixedCellHeight </td><td>list </td><td>yes </td></tr>
91 * <tr><td>fixedCellWidth </td><td>list </td><td>yes </td></tr>
92 * <tr><td>lastVisibleIndex </td><td>list </td><td>no </td></tr>
93 * <tr><td>layoutOrientation </td><td>list </td><td>yes </td></tr>
94 * <tr><td>leadSelectionIndex </td><td>selection</td><td>no </td></tr>
95 * <tr><td>maxSelectionIndex </td><td>selection</td><td>no </td></tr>
96 * <tr><td>minSelectionIndex </td><td>selection</td><td>no </td></tr>
97 * <tr><td>model </td><td>list </td><td>yes </td></tr>
98 * <tr><td>opaque </td><td>list </td><td>no </td></tr>
99 * <tr><td>preferredScrollableViewportSize</td><td>list </td><td>no </td></tr>
100 * <tr><td>prototypeCellValue </td><td>list </td><td>yes </td></tr>
101 * <tr><td>scrollableTracksViewportHeight </td><td>list </td><td>no </td></tr>
102 * <tr><td>scrollableTracksViewportWidth </td><td>list </td><td>no </td></tr>
103 * <tr><td>selectedIndex </td><td>selection</td><td>no </td></tr>
104 * <tr><td>selectedIndices </td><td>selection</td><td>no </td></tr>
105 * <tr><td>selectedValue </td><td>model </td><td>no </td></tr>
106 * <tr><td>selectedValues </td><td>model </td><td>no </td></tr>
107 * <tr><td>selectionBackground </td><td>list </td><td>yes </td></tr>
108 * <tr><td>selectionEmpty </td><td>selection</td><td>no </td></tr>
109 * <tr><td>selectionForeground </td><td>list </td><td>yes </td></tr>
110 * <tr><td>selectionMode </td><td>selection</td><td>no </td></tr>
111 * <tr><td>selectionModel </td><td>list </td><td>yes </td></tr>
112 * <tr><td>UI </td><td>list </td><td>yes </td></tr>
113 * <tr><td>UIClassID </td><td>list </td><td>no </td></tr>
114 * <tr><td>valueIsAdjusting </td><td>list </td><td>no </td></tr>
115 * <tr><td>visibleRowCount </td><td>list </td><td>no </td></tr>
118 * @author Graydon Hoare (graydon@redhat.com)
121 public class JList extends JComponent implements Accessible, Scrollable
125 * Provides accessibility support for <code>JList</code>.
127 protected class AccessibleJList extends AccessibleJComponent
128 implements AccessibleSelection, PropertyChangeListener,
129 ListSelectionListener, ListDataListener
133 * Provides accessibility support for list elements in <code>JList</code>s.
135 protected class AccessibleJListChild extends AccessibleContext
136 implements Accessible, AccessibleComponent
145 * The index in the list for that child.
150 * The cursor for this list child.
152 // TODO: Testcases show that this class somehow stores state about the
153 // cursor. I cannot make up though how that could affect
155 Cursor cursor = Cursor.getDefaultCursor();
158 * Creates a new instance of <code>AccessibleJListChild</code>.
160 * @param list the list of which this is an accessible child
161 * @param index the list index for this child
163 public AccessibleJListChild(JList list, int index)
170 * Returns the accessible context of this object. Returns
171 * <code>this</code> since <code>AccessibleJListChild</code>s are their
172 * own accessible contexts.
174 * @return the accessible context of this object, <code>this</code>
176 public AccessibleContext getAccessibleContext()
182 * Returns the background color for this list child. This returns the
183 * background of the <code>JList</code> itself since the background
184 * cannot be set on list children individually
186 * @return the background color for this list child
188 public Color getBackground()
190 return parent.getBackground();
194 * Calling this method has no effect, since the background color cannot be
195 * set on list children individually.
197 * @param color not used here.
199 public void setBackground(Color color)
201 // Calling this method has no effect, since the background color cannot
202 // be set on list children individually.
206 * Returns the foreground color for this list child. This returns the
207 * background of the <code>JList</code> itself since the foreground
208 * cannot be set on list children individually.
210 * @return the background color for this list child
212 public Color getForeground()
214 return parent.getForeground();
218 * Calling this method has no effect, since the foreground color cannot be
219 * set on list children individually.
221 * @param color not used here.
223 public void setForeground(Color color)
225 // Calling this method has no effect, since the foreground color cannot
226 // be set on list children individually.
230 * Returns the cursor for this list child.
232 * @return the cursor for this list child
234 public Cursor getCursor()
236 // TODO: Testcases show that this method returns the cursor that has
237 // been set by setCursor. I cannot make up though how that could affect
243 * Sets the cursor for this list child.
245 public void setCursor(Cursor cursor)
247 this.cursor = cursor;
248 // TODO: Testcases show that this method returns the cursor that has
249 // been set by setCursor. I cannot make up though how that could affect
254 * Returns the font of the <code>JList</code> since it is not possible to
255 * set fonts for list children individually.
257 * @return the font of the <code>JList</code>
259 public Font getFont()
261 return parent.getFont();
265 * Does nothing since it is not possible to set the font on list children
268 * @param font not used here
270 public void setFont(Font font)
272 // Does nothing since it is not possible to set the font on list
273 // children individually.
277 * Returns the font metrics for the specified font. This method forwards
278 * to the parent <code>JList</code>.
280 * @param font the font for which the font metrics is queried
282 * @return the font metrics for the specified font
284 public FontMetrics getFontMetrics(Font font)
286 return parent.getFontMetrics(font);
290 * Returns <code>true</code> if the parent <code>JList</code> is enabled,
291 * <code>false</code> otherwise. The list children cannot have an enabled
292 * flag set individually.
294 * @return <code>true</code> if the parent <code>JList</code> is enabled,
295 * <code>false</code> otherwise
297 public boolean isEnabled()
299 return parent.isEnabled();
303 * Does nothing since the enabled flag cannot be set for list children
306 * @param b not used here
308 public void setEnabled(boolean b)
310 // Does nothing since the enabled flag cannot be set for list children
315 * Returns <code>true</code> if this list child is visible,
316 * <code>false</code> otherwise. The value of this property depends
317 * on {@link JList#getFirstVisibleIndex()} and
318 * {@link JList#getLastVisibleIndex()}.
320 * @return <code>true</code> if this list child is visible,
321 * <code>false</code> otherwise
323 public boolean isVisible()
325 return listIndex >= parent.getFirstVisibleIndex()
326 && listIndex <= parent.getLastVisibleIndex();
330 * The value of the visible property cannot be modified, so this method
333 * @param b not used here
335 public void setVisible(boolean b)
337 // The value of the visible property cannot be modified, so this method
342 * Returns <code>true</code> if this list child is currently showing on
343 * screen and <code>false</code> otherwise. The list child is showing if
344 * it is visible and if it's parent JList is currently showing.
346 * @return <code>true</code> if this list child is currently showing on
347 * screen and <code>false</code> otherwise
349 public boolean isShowing()
351 return isVisible() && parent.isShowing();
355 * Returns <code>true</code> if this list child covers the screen location
356 * <code>point</code> (relative to the <code>JList</code> coordinate
357 * system, <code>false</code> otherwise.
359 * @return <code>true</code> if this list child covers the screen location
360 * <code>point</code> , <code>false</code> otherwise
362 public boolean contains(Point point)
364 return getBounds().contains(point);
368 * Returns the absolute screen location of this list child.
370 * @return the absolute screen location of this list child
372 public Point getLocationOnScreen()
374 Point loc = getLocation();
375 SwingUtilities.convertPointToScreen(loc, parent);
380 * Returns the screen location of this list child relative to it's parent.
382 * @return the location of this list child relative to it's parent
384 * @see JList#indexToLocation(int)
386 public Point getLocation()
388 return parent.indexToLocation(listIndex);
392 * Does nothing since the screen location cannot be set on list children
395 * @param point not used here
397 public void setLocation(Point point)
399 // Does nothing since the screen location cannot be set on list children
404 * Returns the bounds of this list child.
406 * @return the bounds of this list child
408 * @see JList#getCellBounds(int, int)
410 public Rectangle getBounds()
412 return parent.getCellBounds(listIndex, listIndex);
416 * Does nothing since the bounds cannot be set on list children
419 * @param rectangle not used here
421 public void setBounds(Rectangle rectangle)
423 // Does nothing since the bounds cannot be set on list children
428 * Returns the size of this list child.
430 * @return the size of this list child
432 public Dimension getSize()
434 Rectangle b = getBounds();
439 * Does nothing since the size cannot be set on list children
442 * @param dimension not used here
444 public void setSize(Dimension dimension)
446 // Does nothing since the size cannot be set on list children
451 * Returns <code>null</code> because list children do not have children
454 * @return <code>null</code>
456 public Accessible getAccessibleAt(Point point)
462 * Returns <code>true</code> since list children are focus traversable.
466 public boolean isFocusTraversable()
468 // TODO: Is this 100% ok?
473 * Requests focus on the parent list. List children cannot request focus
476 public void requestFocus()
478 // TODO: Is this 100% ok?
479 parent.requestFocus();
483 * Adds a focus listener to the parent list. List children do not have
484 * their own focus management.
486 * @param listener the focus listener to add
488 public void addFocusListener(FocusListener listener)
490 // TODO: Is this 100% ok?
491 parent.addFocusListener(listener);
495 * Removes a focus listener from the parent list. List children do not
496 * have their own focus management.
498 * @param listener the focus listener to remove
500 public void removeFocusListener(FocusListener listener)
502 // TODO: Is this 100%
503 parent.removeFocusListener(listener);
507 * Returns the accessible role of this list item, which is
508 * {@link AccessibleRole#LABEL}.
510 * @return {@link AccessibleRole#LABEL}
512 public AccessibleRole getAccessibleRole()
514 return AccessibleRole.LABEL;
518 * Returns the accessible state set of this list item.
520 * @return the accessible state set of this list item
522 public AccessibleStateSet getAccessibleStateSet()
524 AccessibleStateSet states = new AccessibleStateSet();
526 states.add(AccessibleState.VISIBLE);
528 states.add(AccessibleState.SHOWING);
529 if (isFocusTraversable())
530 states.add(AccessibleState.FOCUSABLE);
531 // TODO: How should the active state be handled? The API docs
532 // suggest that this state is set on the activated list child,
533 // that is the one that is drawn with a box. However, I don't know how
534 // to implement this.
536 // TODO: We set the selectable state here because list children are
537 // selectable. Is there a way to disable single children?
538 if (parent.isEnabled())
539 states.add(AccessibleState.SELECTABLE);
541 if (parent.isSelectedIndex(listIndex))
542 states.add(AccessibleState.SELECTED);
544 // TODO: Handle more states here?
549 * Returns the index of this list child within it's parent list.
551 * @return the index of this list child within it's parent list
553 public int getAccessibleIndexInParent()
559 * Returns <code>0</code> since list children don't have children
562 * @return <code>0</code>
564 public int getAccessibleChildrenCount()
570 * Returns <code>null</code> since list children don't have children
573 * @return <code>null</code>
575 public Accessible getAccessibleChild(int i)
581 * Returns the locale of this component. This call is forwarded to the
582 * parent list since list children don't have a separate locale setting.
584 * @return the locale of this component
586 public Locale getLocale()
588 return parent.getLocale();
593 * nothing, list children are transient accessible objects which means
594 * that they don't fire property change events.
596 * @param l not used here
598 public void addPropertyChangeListener(PropertyChangeListener l)
605 * nothing, list children are transient accessible objects which means
606 * that they don't fire property change events.
608 * @param l not used here
610 public void removePropertyChangeListener(PropertyChangeListener l)
615 // TODO: Implement the remaining methods of this class.
619 * Create a new AccessibleJList.
621 public AccessibleJList()
623 // Nothing to do here.
627 * Returns the number of selected accessible children.
629 * @return the number of selected accessible children
631 public int getAccessibleSelectionCount()
633 return getSelectedIndices().length;
637 * Returns the n-th selected accessible child.
639 * @param n the index of the selected child to return
641 * @return the n-th selected accessible child
643 public Accessible getAccessibleSelection(int n)
645 return new AccessibleJListChild(JList.this, getSelectedIndices()[n]);
649 * Returns <code>true</code> if the n-th child is selected,
650 * <code>false</code> otherwise.
652 * @param n the index of the child of which the selected state is queried
654 * @return <code>true</code> if the n-th child is selected,
655 * <code>false</code> otherwise
657 public boolean isAccessibleChildSelected(int n)
659 return isSelectedIndex(n);
663 * Adds the accessible item with the specified index to the selected items.
664 * If multiple selections are supported, the item is added to the selection,
665 * otherwise the item replaces the current selection.
667 * @param i the index of the item to add to the selection
669 public void addAccessibleSelection(int i)
671 addSelectionInterval(i, i);
675 * Removes the accessible item with the specified index to the selection.
677 * @param i the index of the item to be removed from the selection
679 public void removeAccessibleSelection(int i)
681 removeSelectionInterval(i, i);
685 * Remove all selection items from the selection.
687 public void clearAccessibleSelection()
693 * Selects all items if multiple selections are supported.
694 * Otherwise do nothing.
696 public void selectAllAccessibleSelection()
698 addSelectionInterval(0, getModel().getSize());
702 * Receices notification when the list selection is changed. This method
703 * fires two property change events, the first with
704 * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY} and the second
705 * with {@link AccessibleContext#ACCESSIBLE_SELECTION_PROPERTY}.
707 * @param event the list selection event
709 public void valueChanged(ListSelectionEvent event)
711 firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE,
713 firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, Boolean.FALSE,
718 * Receives notification when items have changed in the
719 * <code>JList</code>. This method fires a property change event with
720 * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}.
722 * @param event the list data event
724 public void contentsChanged(ListDataEvent event)
726 firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE,
731 * Receives notification when items are inserted into the
732 * <code>JList</code>. This method fires a property change event with
733 * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}.
735 * @param event the list data event
737 public void intervalAdded(ListDataEvent event)
739 firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE,
744 * Receives notification when items are removed from the
745 * <code>JList</code>. This method fires a property change event with
746 * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}.
748 * @param event the list data event
750 public void intervalRemoved(ListDataEvent event)
752 firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE,
758 * Receives notification about changes of the <code>JList</code>'s
759 * properties. This is used to re-register this object as listener to
760 * the data model and selection model when the data model or selection model
763 * @param e the property change event
765 public void propertyChange(PropertyChangeEvent e)
767 String propertyName = e.getPropertyName();
768 if (propertyName.equals("model"))
770 ListModel oldModel = (ListModel) e.getOldValue();
771 oldModel.removeListDataListener(this);
772 ListModel newModel = (ListModel) e.getNewValue();
773 newModel.addListDataListener(this);
775 else if (propertyName.equals("selectionModel"))
777 ListSelectionModel oldModel = (ListSelectionModel) e.getOldValue();
778 oldModel.removeListSelectionListener(this);
779 ListSelectionModel newModel = (ListSelectionModel) e.getNewValue();
780 oldModel.addListSelectionListener(this);
785 * Return the state set of the <code>JList</code>.
787 * @return the state set of the <code>JList</code>
789 public AccessibleStateSet getAccessibleStateSet()
791 // TODO: Figure out if there is possibly more state that must be
793 AccessibleStateSet s = super.getAccessibleStateSet();
794 if (getSelectionMode() != ListSelectionModel.SINGLE_SELECTION)
795 s.add(AccessibleState.MULTISELECTABLE);
800 * Returns the accessible role for <code>JList</code>,
801 * {@link AccessibleRole#LIST}.
803 * @return the accessible role for <code>JList</code>
805 public AccessibleRole getAccessibleRole()
807 return AccessibleRole.LIST;
811 * Returns the accessible child at the visual location <code>p</code>
812 * (relative to the upper left corner of the <code>JList</code>). If there
813 * is no child at that location, this returns <code>null</code>.
815 * @param p the screen location for which to return the accessible child
817 * @return the accessible child at the specified location, or
818 * <code>null</code> if there is no child at that location
820 public Accessible getAccessibleAt(Point p)
822 int childIndex = locationToIndex(p);
823 return getAccessibleChild(childIndex);
827 * Returns the number of accessible children in the <code>JList</code>.
829 * @return the number of accessible children in the <code>JList</code>
831 public int getAccessibleChildrenCount()
833 return getModel().getSize();
837 * Returns the n-th accessible child of this <code>JList</code>. This will
838 * be an instance of {@link AccessibleJListChild}. If there is no child
839 * at that index, <code>null</code> is returned.
841 * @param n the index of the child to return
843 * @return the n-th accessible child of this <code>JList</code>
845 public Accessible getAccessibleChild(int n)
847 if (getModel().getSize() <= n)
849 return new AccessibleJListChild(JList.this, n);
853 private static final long serialVersionUID = 4406629526391098046L;
856 * Constant value used in "layoutOrientation" property. This value means
857 * that cells are laid out in a single vertical column. This is the default.
859 public static final int VERTICAL = 0;
862 * Constant value used in "layoutOrientation" property. This value means
863 * that cells are laid out in multiple columns "newspaper style", filling
864 * vertically first, then horizontally.
866 public static final int VERTICAL_WRAP = 1;
869 * Constant value used in "layoutOrientation" property. This value means
870 * that cells are laid out in multiple columns "newspaper style",
871 * filling horizontally first, then vertically.
873 public static final int HORIZONTAL_WRAP = 2;
876 * This property indicates whether "drag and drop" functions are enabled
881 /** This property provides a strategy for rendering cells in the list. */
882 ListCellRenderer cellRenderer;
885 * This property indicates an fixed width to assign to all cells in the
886 * list. If its value is <code>-1</code>, no width has been
887 * assigned. This value can be set explicitly, or implicitly by setting
888 * the {@link #prototypeCellValue} property.
893 * This property indicates an fixed height to assign to all cells in the
894 * list. If its value is <code>-1</code>, no height has been
895 * assigned. This value can be set explicitly, or implicitly by setting
896 * the {@link #prototypeCellValue} property.
901 * This property holds the current layout orientation of the list, which
902 * is one of the integer constants {@link #VERTICAL}, {@link
903 * #VERTICAL_WRAP}, or {@link #HORIZONTAL_WRAP}.
905 int layoutOrientation;
907 /** This property holds the data elements displayed by the list. */
911 * <p>This property holds a reference to a "prototype" data value --
912 * typically a String -- which is used to calculate the {@link
913 * #fixedCellWidth} and {@link #fixedCellHeight} properties, using the
914 * {@link #cellRenderer} property to acquire a component to render the
917 * <p>It is important that you <em>not</em> set this value to a
918 * component. It has to be a <em>data value</em> such as the objects you
919 * would find in the list's model. Setting it to a component will have
920 * undefined (and undesirable) affects. </p>
922 Object prototypeCellValue;
925 * This property specifies a foreground color for the selected cells in
926 * the list. When {@link ListCellRenderer#getListCellRendererComponent}
927 * is called with a selected cell object, the component returned will
928 * have its "foreground" set to this color.
930 Color selectionBackground;
933 * This property specifies a background color for the selected cells in
934 * the list. When {@link ListCellRenderer#getListCellRendererComponent}
935 * is called with a selected cell object, the component returned will
936 * have its "background" property set to this color.
938 Color selectionForeground;
941 * This property holds a description of which data elements in the {@link
942 * #model} property should be considered "selected", when displaying and
943 * interacting with the list.
945 ListSelectionModel selectionModel;
948 * This property indicates a <em>preference</em> for the number of rows
949 * displayed in the list, and will scale the
950 * {@link #getPreferredScrollableViewportSize} property accordingly. The actual
951 * number of displayed rows, when the list is placed in a real {@link
952 * JViewport} or other component, may be greater or less than this number.
957 * Fire a {@link ListSelectionEvent} to all the registered
958 * ListSelectionListeners.
960 * @param firstIndex the lowest index covering the selection change.
961 * @param lastIndex the highest index covering the selection change.
962 * @param isAdjusting a flag indicating if this event is one in a series
963 * of events updating the selection.
965 protected void fireSelectionValueChanged(int firstIndex, int lastIndex,
968 ListSelectionEvent evt = new ListSelectionEvent(this, firstIndex,
969 lastIndex, isAdjusting);
970 ListSelectionListener listeners[] = getListSelectionListeners();
971 for (int i = 0; i < listeners.length; ++i)
973 listeners[i].valueChanged(evt);
978 * This private listener propagates {@link ListSelectionEvent} events
979 * from the list's "selectionModel" property to the list's {@link
980 * ListSelectionListener} listeners. It also listens to {@link
981 * ListDataEvent} events from the list's {@link #model} property. If this
982 * class receives either type of event, it triggers repainting of the
985 private class ListListener
986 implements ListSelectionListener, ListDataListener
988 // ListDataListener events
989 public void contentsChanged(ListDataEvent event)
991 JList.this.revalidate();
992 JList.this.repaint();
994 public void intervalAdded(ListDataEvent event)
996 JList.this.revalidate();
997 JList.this.repaint();
999 public void intervalRemoved(ListDataEvent event)
1001 JList.this.revalidate();
1002 JList.this.repaint();
1004 // ListSelectionListener events
1005 public void valueChanged(ListSelectionEvent event)
1007 JList.this.fireSelectionValueChanged(event.getFirstIndex(),
1008 event.getLastIndex(),
1009 event.getValueIsAdjusting());
1010 JList.this.repaint();
1015 * Shared ListListener instance, subscribed to both the current {@link
1016 * #model} and {@link #selectionModel} properties of the list.
1018 ListListener listListener;
1022 * Creates a new <code>JList</code> object.
1026 init(new DefaultListModel());
1030 * Creates a new <code>JList</code> object.
1032 * @param items the initial list items.
1034 public JList(Object[] items)
1036 init(createListModel(items));
1040 * Creates a new <code>JList</code> object.
1042 * @param items the initial list items.
1044 public JList(Vector<?> items)
1046 init(createListModel(items));
1050 * Creates a new <code>JList</code> object.
1052 * @param model a model containing the list items (<code>null</code> not
1055 * @throws IllegalArgumentException if <code>model</code> is
1056 * <code>null</code>.
1058 public JList(ListModel model)
1064 * Initializes the list.
1066 * @param m the list model (<code>null</code> not permitted).
1068 private void init(ListModel m)
1071 throw new IllegalArgumentException("Null model not permitted.");
1072 dragEnabled = false;
1073 fixedCellHeight = -1;
1074 fixedCellWidth = -1;
1075 layoutOrientation = VERTICAL;
1077 visibleRowCount = 8;
1079 cellRenderer = new DefaultListCellRenderer();
1080 listListener = new ListListener();
1084 model.addListDataListener(listListener);
1086 selectionModel = createSelectionModel();
1087 if (selectionModel != null)
1089 selectionModel.addListSelectionListener(listListener);
1090 selectionModel.setSelectionMode
1091 (ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
1099 * Creates the default <code>ListSelectionModel</code>.
1101 * @return the <code>ListSelectionModel</code>
1103 protected ListSelectionModel createSelectionModel()
1105 return new DefaultListSelectionModel();
1109 * Gets the value of the {@link #fixedCellHeight} property. This property
1110 * may be <code>-1</code> to indicate that no cell height has been
1111 * set. This property is also set implicitly when the
1112 * {@link #prototypeCellValue} property is set.
1114 * @return The current value of the property
1116 * @see #fixedCellHeight
1117 * @see #setFixedCellHeight
1118 * @see #setPrototypeCellValue
1120 public int getFixedCellHeight()
1122 return fixedCellHeight;
1126 * Sets the value of the {@link #fixedCellHeight} property. This property
1127 * may be <code>-1</code> to indicate that no cell height has been
1128 * set. This property is also set implicitly when the {@link
1129 * #prototypeCellValue} property is set, but setting it explicitly
1130 * overrides the height computed from {@link #prototypeCellValue}.
1132 * @param h the height.
1134 * @see #getFixedCellHeight
1135 * @see #getPrototypeCellValue
1137 public void setFixedCellHeight(int h)
1139 if (fixedCellHeight == h)
1142 int old = fixedCellHeight;
1143 fixedCellHeight = h;
1144 firePropertyChange("fixedCellHeight", old, h);
1149 * Gets the value of the {@link #fixedCellWidth} property. This property
1150 * may be <code>-1</code> to indicate that no cell width has been
1151 * set. This property is also set implicitly when the {@link
1152 * #prototypeCellValue} property is set.
1154 * @return The current value of the property
1156 * @see #setFixedCellWidth
1157 * @see #setPrototypeCellValue
1159 public int getFixedCellWidth()
1161 return fixedCellWidth;
1165 * Sets the value of the {@link #fixedCellWidth} property. This property
1166 * may be <code>-1</code> to indicate that no cell width has been
1167 * set. This property is also set implicitly when the {@link
1168 * #prototypeCellValue} property is set, but setting it explicitly
1169 * overrides the width computed from {@link #prototypeCellValue}.
1171 * @param w the width.
1173 * @see #getFixedCellHeight
1174 * @see #getPrototypeCellValue
1176 public void setFixedCellWidth(int w)
1178 if (fixedCellWidth == w)
1181 int old = fixedCellWidth;
1183 firePropertyChange("fixedCellWidth", old, w);
1187 * Gets the value of the {@link #visibleRowCount} property. The default
1190 * @return the current value of the property.
1192 * @see #setVisibleRowCount(int)
1194 public int getVisibleRowCount()
1196 return visibleRowCount;
1200 * Sets the value of the {@link #visibleRowCount} property.
1202 * @param vc The new property value
1204 * @see #getVisibleRowCount()
1206 public void setVisibleRowCount(int vc)
1208 if (visibleRowCount != vc)
1210 int oldValue = visibleRowCount;
1211 visibleRowCount = Math.max(vc, 0);
1212 firePropertyChange("visibleRowCount", oldValue, vc);
1219 * Adds a {@link ListSelectionListener} to the listener list for this
1220 * list. The listener will be called back with a {@link
1221 * ListSelectionEvent} any time the list's {@link #selectionModel}
1222 * property changes. The source of such events will be the JList,
1223 * not the selection model.
1225 * @param listener The new listener to add
1227 public void addListSelectionListener(ListSelectionListener listener)
1229 listenerList.add (ListSelectionListener.class, listener);
1233 * Removes a {@link ListSelectionListener} from the listener list for
1234 * this list. The listener will no longer be called when the list's
1235 * {@link #selectionModel} changes.
1237 * @param listener The listener to remove
1239 public void removeListSelectionListener(ListSelectionListener listener)
1241 listenerList.remove(ListSelectionListener.class, listener);
1245 * Returns an array of all ListSelectionListeners subscribed to this
1248 * @return The current subscribed listeners
1252 public ListSelectionListener[] getListSelectionListeners()
1254 return (ListSelectionListener[]) getListeners(ListSelectionListener.class);
1258 * Returns the selection mode for the list (one of:
1259 * {@link ListSelectionModel#SINGLE_SELECTION},
1260 * {@link ListSelectionModel#SINGLE_INTERVAL_SELECTION} and
1261 * {@link ListSelectionModel#MULTIPLE_INTERVAL_SELECTION}).
1263 * @return The selection mode.
1265 * @see #setSelectionMode(int)
1267 public int getSelectionMode()
1269 return selectionModel.getSelectionMode();
1273 * Sets the list's "selectionMode" property, which simply mirrors the
1274 * same property on the list's {@link #selectionModel} property. This
1275 * property should be one of the integer constants
1276 * <code>SINGLE_SELECTION</code>, <code>SINGLE_INTERVAL_SELECTION</code>,
1277 * or <code>MULTIPLE_INTERVAL_SELECTION</code> from the {@link
1278 * ListSelectionModel} interface.
1280 * @param a The new selection mode
1282 public void setSelectionMode(int a)
1284 selectionModel.setSelectionMode(a);
1288 * Adds the interval <code>[a,a]</code> to the set of selections managed
1289 * by this list's {@link #selectionModel} property. Depending on the
1290 * selection mode, this may cause existing selections to become invalid,
1291 * or may simply expand the set of selections.
1293 * @param a A number in the half-open range <code>[0, x)</code> where
1294 * <code>x = getModel.getSize()</code>, indicating the index of an
1295 * element in the list to select. When < 0 the selection is cleared.
1297 * @see #setSelectionMode
1298 * @see #selectionModel
1300 public void setSelectedIndex(int a)
1303 selectionModel.clearSelection();
1305 selectionModel.setSelectionInterval(a, a);
1309 * For each element <code>a[i]</code> of the provided array
1310 * <code>a</code>, calls {@link #setSelectedIndex} on <code>a[i]</code>.
1312 * @param a an array of selected indices (<code>null</code> not permitted).
1314 * @throws NullPointerException if <code>a</code> is <code>null</code>.
1315 * @see #setSelectionMode
1316 * @see #selectionModel
1318 public void setSelectedIndices(int [] a)
1320 for (int i = 0; i < a.length; ++i)
1321 setSelectedIndex(a[i]);
1325 * Returns the minimum index of an element in the list which is currently
1328 * @return A number in the half-open range <code>[0, x)</code> where
1329 * <code>x = getModel.getSize()</code>, indicating the minimum index of
1330 * an element in the list for which the element is selected, or
1331 * <code>-1</code> if no elements are selected
1333 public int getSelectedIndex()
1335 return selectionModel.getMinSelectionIndex();
1339 * Returns <code>true</code> if the model's selection is empty, otherwise
1340 * <code>false</code>.
1342 * @return The return value of {@link ListSelectionModel#isSelectionEmpty}
1344 public boolean isSelectionEmpty()
1346 return selectionModel.isSelectionEmpty();
1350 * Returns the list index of the upper left or upper right corner of the
1351 * visible rectangle of this list, depending on the {@link
1352 * Component#getComponentOrientation} property.
1354 * @return The index of the first visible list cell, or <code>-1</code>
1355 * if none is visible.
1357 public int getFirstVisibleIndex()
1359 ComponentOrientation or = getComponentOrientation();
1360 Rectangle r = getVisibleRect();
1361 if (or == ComponentOrientation.RIGHT_TO_LEFT)
1362 r.translate((int) r.getWidth() - 1, 0);
1363 return getUI().locationToIndex(this, r.getLocation());
1368 * Returns index of the cell to which specified location is closest to. If
1369 * the location is outside the bounds of the list, then the greatest index
1370 * in the list model is returned. If the list model is empty, then
1371 * <code>-1</code> is returned.
1373 * @param location for which to look for in the list
1375 * @return index of the cell to which specified location is closest to.
1377 public int locationToIndex(Point location)
1379 return getUI().locationToIndex(this, location);
1383 * Returns location of the cell located at the specified index in the list.
1384 * @param index of the cell for which location will be determined
1386 * @return location of the cell located at the specified index in the list.
1388 public Point indexToLocation(int index)
1390 return getUI().indexToLocation(this, index);
1394 * Returns the list index of the lower right or lower left corner of the
1395 * visible rectangle of this list, depending on the {@link
1396 * Component#getComponentOrientation} property.
1398 * @return The index of the last visible list cell, or <code>-1</code>
1399 * if none is visible.
1401 public int getLastVisibleIndex()
1403 ComponentOrientation or = getComponentOrientation();
1404 Rectangle r = getVisibleRect();
1405 r.translate(0, (int) r.getHeight() - 1);
1406 if (or == ComponentOrientation.LEFT_TO_RIGHT)
1407 r.translate((int) r.getWidth() - 1, 0);
1408 if (getUI().locationToIndex(this, r.getLocation()) == -1
1409 && indexToLocation(getModel().getSize() - 1).y < r.y)
1410 return getModel().getSize() - 1;
1411 return getUI().locationToIndex(this, r.getLocation());
1415 * Returns the indices of values in the {@link #model} property which are
1418 * @return An array of model indices, each of which is selected according
1419 * to the {@link #getSelectedValues} property
1421 public int[] getSelectedIndices()
1423 int lo, hi, n, i, j;
1424 if (selectionModel.isSelectionEmpty())
1426 lo = selectionModel.getMinSelectionIndex();
1427 hi = selectionModel.getMaxSelectionIndex();
1429 for (i = lo; i <= hi; ++i)
1430 if (selectionModel.isSelectedIndex(i))
1432 int [] v = new int[n];
1434 for (i = lo; i <= hi; ++i)
1435 if (selectionModel.isSelectedIndex(i))
1441 * Indicates whether the list element at a given index value is
1442 * currently selected.
1444 * @param a The index to check
1445 * @return <code>true</code> if <code>a</code> is the index of a selected
1448 public boolean isSelectedIndex(int a)
1450 return selectionModel.isSelectedIndex(a);
1454 * Returns the first value in the list's {@link #model} property which is
1455 * selected, according to the list's {@link #selectionModel} property.
1456 * This is equivalent to calling
1457 * <code>getModel()getElementAt(getSelectedIndex())</code>, with a check
1458 * for the special index value of <code>-1</code> which returns null
1459 * <code>null</code>.
1461 * @return The first selected element, or <code>null</code> if no element
1464 * @see #getSelectedValues
1466 public Object getSelectedValue()
1468 int index = getSelectedIndex();
1471 return getModel().getElementAt(index);
1475 * Returns all the values in the list's {@link #model} property which are
1476 * selected, according to the list's {@link #selectionModel} property.
1478 * @return An array containing all the selected values
1479 * @see #setSelectedValue
1481 public Object[] getSelectedValues()
1483 int[] idx = getSelectedIndices();
1484 Object[] v = new Object[idx.length];
1485 for (int i = 0; i < idx.length; ++i)
1486 v[i] = getModel().getElementAt(idx[i]);
1491 * Gets the value of the {@link #selectionBackground} property.
1493 * @return The current value of the property
1495 public Color getSelectionBackground()
1497 return selectionBackground;
1501 * Sets the value of the {@link #selectionBackground} property.
1503 * @param c The new value of the property
1505 public void setSelectionBackground(Color c)
1507 if (selectionBackground == c)
1510 Color old = selectionBackground;
1511 selectionBackground = c;
1512 firePropertyChange("selectionBackground", old, c);
1517 * Gets the value of the {@link #selectionForeground} property.
1519 * @return The current value of the property
1521 public Color getSelectionForeground()
1523 return selectionForeground;
1527 * Sets the value of the {@link #selectionForeground} property.
1529 * @param c The new value of the property
1531 public void setSelectionForeground(Color c)
1533 if (selectionForeground == c)
1536 Color old = selectionForeground;
1537 selectionForeground = c;
1538 firePropertyChange("selectionForeground", old, c);
1542 * Sets the selection to cover only the specified value, if it
1543 * exists in the model.
1545 * @param obj The object to select
1546 * @param scroll Whether to scroll the list to make the newly selected
1549 * @see #ensureIndexIsVisible
1552 public void setSelectedValue(Object obj, boolean scroll)
1554 for (int i = 0; i < model.getSize(); ++i)
1556 if (model.getElementAt(i).equals(obj))
1558 setSelectedIndex(i);
1560 ensureIndexIsVisible(i);
1567 * Scrolls this list to make the specified cell visible. This
1568 * only works if the list is contained within a viewport.
1570 * @param i The list index to make visible
1572 * @see JComponent#scrollRectToVisible
1574 public void ensureIndexIsVisible(int i)
1576 Rectangle r = getUI().getCellBounds(this, i, i);
1578 scrollRectToVisible(r);
1582 * Sets the {@link #model} property of the list to a new anonymous
1583 * {@link AbstractListModel} subclass which accesses the provided Object
1586 * @param listData The object array to build a new list model on
1589 public void setListData(Object[] listData)
1591 setModel(createListModel(listData));
1595 * Returns a {@link ListModel} backed by the specified array.
1597 * @param items the list items (don't use <code>null</code>).
1599 * @return A list model containing the specified items.
1601 private ListModel createListModel(final Object[] items)
1603 return new AbstractListModel()
1605 public int getSize()
1607 return items.length;
1609 public Object getElementAt(int i)
1617 * Returns a {@link ListModel} backed by the specified vector.
1619 * @param items the list items (don't use <code>null</code>).
1621 * @return A list model containing the specified items.
1623 private ListModel createListModel(final Vector items)
1625 return new AbstractListModel()
1627 public int getSize()
1629 return items.size();
1631 public Object getElementAt(int i)
1633 return items.get(i);
1639 * Sets the {@link #model} property of the list to a new anonymous {@link
1640 * AbstractListModel} subclass which accesses the provided vector
1643 * @param listData The object array to build a new list model on
1646 public void setListData(final Vector<?> listData)
1648 setModel(new AbstractListModel()
1650 public int getSize()
1652 return listData.size();
1655 public Object getElementAt(int i)
1657 return listData.elementAt(i);
1663 * Gets the value of the {@link #cellRenderer} property.
1665 * @return The current value of the property
1667 public ListCellRenderer getCellRenderer()
1669 return cellRenderer;
1673 * Sets the value of the {@link #getCellRenderer} property.
1675 * @param renderer The new property value
1677 public void setCellRenderer(ListCellRenderer renderer)
1679 if (cellRenderer == renderer)
1682 ListCellRenderer old = cellRenderer;
1683 cellRenderer = renderer;
1684 firePropertyChange("cellRenderer", old, renderer);
1690 * Gets the value of the {@link #model} property.
1692 * @return The current value of the property
1694 public ListModel getModel()
1700 * Sets the value of the {@link #model} property. The list's {@link
1701 * #listListener} is unsubscribed from the existing model, if it exists,
1702 * and re-subscribed to the new model.
1704 * @param model the new model (<code>null</code> not permitted).
1706 * @throws IllegalArgumentException if <code>model</code> is
1707 * <code>null</code>.
1709 public void setModel(ListModel model)
1712 throw new IllegalArgumentException("Null 'model' argument.");
1713 if (this.model == model)
1716 if (this.model != null)
1717 this.model.removeListDataListener(listListener);
1719 ListModel old = this.model;
1722 if (this.model != null)
1723 this.model.addListDataListener(listListener);
1725 firePropertyChange("model", old, model);
1731 * Returns the selection model for the {@link JList} component. Note that
1732 * this class contains a range of convenience methods for configuring the
1733 * selection model:<br>
1735 * <li>{@link #clearSelection()};</li>
1736 * <li>{@link #setSelectionMode(int)};</li>
1737 * <li>{@link #addSelectionInterval(int, int)};</li>
1738 * <li>{@link #setSelectedIndex(int)};</li>
1739 * <li>{@link #setSelectedIndices(int[])};</li>
1740 * <li>{@link #setSelectionInterval(int, int)}.</li>
1743 * @return The selection model.
1745 public ListSelectionModel getSelectionModel()
1747 return selectionModel;
1751 * Sets the value of the {@link #selectionModel} property. The list's
1752 * {@link #listListener} is unsubscribed from the existing selection
1753 * model, if it exists, and re-subscribed to the new selection model.
1755 * @param model The new property value
1757 public void setSelectionModel(ListSelectionModel model)
1759 if (selectionModel == model)
1762 if (selectionModel != null)
1763 selectionModel.removeListSelectionListener(listListener);
1765 ListSelectionModel old = selectionModel;
1766 selectionModel = model;
1768 if (selectionModel != null)
1769 selectionModel.addListSelectionListener(listListener);
1771 firePropertyChange("selectionModel", old, model);
1777 * Gets the value of the UI property.
1779 * @return The current property value
1781 public ListUI getUI()
1787 * Sets the value of the UI property.
1789 * @param ui The new property value
1791 public void setUI(ListUI ui)
1797 * Calls {@link #setUI} with the {@link ListUI} subclass
1798 * returned from calling {@link UIManager#getUI}.
1800 public void updateUI()
1802 setUI((ListUI) UIManager.getUI(this));
1806 * Return the class identifier for the list's UI property. This should
1807 * be the constant string <code>"ListUI"</code>, and map to an
1808 * appropriate UI class in the {@link UIManager}.
1810 * @return The class identifier
1812 public String getUIClassID()
1819 * Returns the current value of the {@link #prototypeCellValue}
1820 * property. This property holds a reference to a "prototype" data value
1821 * -- typically a String -- which is used to calculate the {@link
1822 * #fixedCellWidth} and {@link #fixedCellHeight} properties, using the
1823 * {@link #cellRenderer} property to acquire a component to render the
1826 * @return The current prototype cell value
1827 * @see #setPrototypeCellValue
1829 public Object getPrototypeCellValue()
1831 return prototypeCellValue;
1835 * <p>Set the {@link #prototypeCellValue} property. This property holds a
1836 * reference to a "prototype" data value -- typically a String -- which
1837 * is used to calculate the {@link #fixedCellWidth} and {@link
1838 * #fixedCellHeight} properties, using the {@link #cellRenderer} property
1839 * to acquire a component to render the prototype.</p>
1841 * <p>It is important that you <em>not</em> set this value to a
1842 * component. It has to be a <em>data value</em> such as the objects you
1843 * would find in the list's model. Setting it to a component will have
1844 * undefined (and undesirable) affects. </p>
1846 * @param obj The new prototype cell value
1847 * @see #getPrototypeCellValue
1849 public void setPrototypeCellValue(Object obj)
1851 if (prototypeCellValue == obj)
1854 Object old = prototypeCellValue;
1855 Component comp = getCellRenderer()
1856 .getListCellRendererComponent(this, obj, 0, false, false);
1857 Dimension d = comp.getPreferredSize();
1858 fixedCellWidth = d.width;
1859 fixedCellHeight = d.height;
1860 prototypeCellValue = obj;
1861 firePropertyChange("prototypeCellValue", old, obj);
1864 public AccessibleContext getAccessibleContext()
1866 return new AccessibleJList();
1870 * Returns a size indicating how much space this list would like to
1871 * consume, when contained in a scrollable viewport. This is part of the
1872 * {@link Scrollable} interface, which interacts with {@link
1873 * ScrollPaneLayout} and {@link JViewport} to define scrollable objects.
1875 * @return The preferred size
1877 public Dimension getPreferredScrollableViewportSize()
1879 //If the layout orientation is not VERTICAL, then this will
1880 //return the value from getPreferredSize. The current ListUI is
1881 //expected to override getPreferredSize to return an appropriate value.
1882 if (getLayoutOrientation() != VERTICAL)
1883 return getPreferredSize();
1885 int size = getModel().getSize();
1887 // Trivial case: if fixedCellWidth and fixedCellHeight were set
1889 if (fixedCellHeight != -1 && fixedCellWidth != -1)
1890 return new Dimension(fixedCellWidth, size * fixedCellHeight);
1892 // If the model is empty we use 16 * the number of visible rows
1893 // for the height and either fixedCellWidth (if set) or 256
1897 if (fixedCellWidth == -1)
1898 return new Dimension(256, 16 * getVisibleRowCount());
1900 return new Dimension(fixedCellWidth, 16 * getVisibleRowCount());
1903 // Calculate the width: if fixedCellWidth was set use that, otherwise
1904 // use the preferredWidth
1906 if (fixedCellWidth != -1)
1907 prefWidth = fixedCellWidth;
1909 prefWidth = getPreferredSize().width;
1911 // Calculate the height: if fixedCellHeight was set use that, otherwise
1912 // use the height of the first row multiplied by the number of visible
1915 if (fixedCellHeight != -1)
1916 prefHeight = fixedCellHeight;
1918 prefHeight = getVisibleRowCount() * getCellBounds(0, 0).height;
1920 return new Dimension (prefWidth, prefHeight);
1924 * <p>Return the number of pixels the list must scroll in order to move a
1925 * "unit" of the list into the provided visible rectangle. When the
1926 * provided direction is positive, the call describes a "downwards"
1927 * scroll, which will be exposing a cell at a <em>greater</em> index in
1928 * the list than those elements currently showing. Then the provided
1929 * direction is negative, the call describes an "upwards" scroll, which
1930 * will be exposing a cell at a <em>lesser</em> index in the list than
1931 * those elements currently showing.</p>
1933 * <p>If the provided orientation is <code>HORIZONTAL</code>, the above
1934 * comments refer to "rightwards" for positive direction, and "leftwards"
1938 * @param visibleRect The rectangle to scroll an element into
1939 * @param orientation One of the numeric consants <code>VERTICAL</code>
1940 * or <code>HORIZONTAL</code>
1941 * @param direction An integer indicating the scroll direction: positive means
1942 * forwards (down, right), negative means backwards (up, left)
1944 * @return The scrollable unit increment, in pixels
1946 public int getScrollableUnitIncrement(Rectangle visibleRect,
1947 int orientation, int direction)
1950 if (orientation == SwingConstants.VERTICAL)
1952 int row = getFirstVisibleIndex();
1955 else if (direction > 0)
1958 Rectangle bounds = getCellBounds(row, row);
1960 unit = bounds.height - (visibleRect.y - bounds.y);
1967 Rectangle bounds = getCellBounds(row, row);
1969 if (row == 0 && bounds.y == visibleRect.y)
1970 unit = 0; // No need to scroll.
1971 else if (bounds.y == visibleRect.y)
1973 // Scroll to previous row.
1974 Point loc = bounds.getLocation();
1976 int prev = locationToIndex(loc);
1977 Rectangle prevR = getCellBounds(prev, prev);
1978 if (prevR == null || prevR.y >= bounds.y)
1979 unit = 0; // For multicolumn lists.
1981 unit = prevR.height;
1984 unit = visibleRect.y - bounds.y;
1987 else if (orientation == SwingConstants.HORIZONTAL && getLayoutOrientation() != VERTICAL)
1989 // Horizontal scrolling.
1990 int i = locationToIndex(visibleRect.getLocation());
1993 Rectangle b = getCellBounds(i, i);
1996 if (b.x != visibleRect.x)
1999 unit = Math.abs(b.x - visibleRect.x);
2001 unit = b.width + b.x - visibleRect.x;
2011 // This fallback seems to be used by the RI for the degenerate cases
2012 // not covered above.
2014 unit = f != null ? f.getSize() : 1;
2020 * <p>Return the number of pixels the list must scroll in order to move a
2021 * "block" of the list into the provided visible rectangle. When the
2022 * provided direction is positive, the call describes a "downwards"
2023 * scroll, which will be exposing a cell at a <em>greater</em> index in
2024 * the list than those elements currently showing. Then the provided
2025 * direction is negative, the call describes an "upwards" scroll, which
2026 * will be exposing a cell at a <em>lesser</em> index in the list than
2027 * those elements currently showing.</p>
2029 * <p>If the provided orientation is <code>HORIZONTAL</code>, the above
2030 * comments refer to "rightwards" for positive direction, and "leftwards"
2034 * @param visibleRect The rectangle to scroll an element into
2035 * @param orientation One of the numeric consants <code>VERTICAL</code>
2036 * or <code>HORIZONTAL</code>
2037 * @param direction An integer indicating the scroll direction: positive means
2038 * forwards (down, right), negative means backwards (up, left)
2040 * @return The scrollable unit increment, in pixels
2042 public int getScrollableBlockIncrement(Rectangle visibleRect,
2043 int orientation, int direction)
2046 if (orientation == SwingConstants.VERTICAL)
2048 // Default block scroll. Special cases are handled below for
2049 // better usability.
2050 block = visibleRect.height;
2054 // Scroll so that after scrolling the last line aligns with
2055 // the lower boundary of the visible area.
2056 Point p = new Point(visibleRect.x,
2057 visibleRect.y + visibleRect.height - 1);
2058 int last = locationToIndex(p);
2061 Rectangle lastR = getCellBounds(last, last);
2064 block = lastR.y - visibleRect.y;
2065 if (block == 0&& last < getModel().getSize() - 1)
2066 block = lastR.height;
2073 // Scroll so that after scrolling the first line aligns with
2074 // the upper boundary of the visible area.
2075 Point p = new Point(visibleRect.x,
2076 visibleRect.y - visibleRect.height);
2077 int newFirst = locationToIndex(p);
2080 int first = getFirstVisibleIndex();
2082 first = locationToIndex(visibleRect.getLocation());
2083 Rectangle newFirstR = getCellBounds(newFirst, newFirst);
2084 Rectangle firstR = getCellBounds(first, first);
2085 if (newFirstR != null && firstR != null)
2087 // Search first item that would left the current first
2088 // item visible when scrolled to.
2089 while (newFirstR.y + visibleRect.height
2090 < firstR.y + firstR.height
2091 && newFirstR.y < firstR.y)
2094 newFirstR = getCellBounds(newFirst, newFirst);
2096 block = visibleRect.y - newFirstR.y;
2097 if (block <= 0 && newFirstR.y > 0)
2100 newFirstR = getCellBounds(newFirst, newFirst);
2101 if (newFirstR != null)
2102 block = visibleRect.y - newFirstR.y;
2108 else if (orientation == SwingConstants.HORIZONTAL
2109 && getLayoutOrientation() != VERTICAL)
2111 // Default block increment. Special cases are handled below for
2112 // better usability.
2113 block = visibleRect.width;
2117 Point p = new Point(visibleRect.x + visibleRect.width + 1,
2119 int last = locationToIndex(p);
2122 Rectangle lastR = getCellBounds(last, last);
2125 block = lastR.x - visibleRect.x;
2127 block += lastR.width;
2128 else if (block == 0 && last < getModel().getSize() - 1)
2129 block = lastR.width;
2136 Point p = new Point(visibleRect.x - visibleRect.width,
2138 int first = locationToIndex(p);
2141 Rectangle firstR = getCellBounds(first, first);
2144 if (firstR.x < visibleRect.x - visibleRect.width)
2146 if (firstR.x + firstR.width > visibleRect.x)
2147 block = visibleRect.x - firstR.x;
2149 block = visibleRect.x - firstR.x - firstR.width;
2152 block = visibleRect.x - firstR.x;
2162 * Gets the value of the <code>scrollableTracksViewportWidth</code> property.
2164 * @return <code>true</code> if the viewport is larger (horizontally)
2165 * than the list and the list should be expanded to fit the viewport;
2166 * <code>false</code> if the viewport is smaller than the list and the
2167 * list should scroll (horizontally) within the viewport
2169 public boolean getScrollableTracksViewportWidth()
2171 Component parent = getParent();
2172 boolean retVal = false;
2173 if (parent instanceof JViewport)
2175 JViewport viewport = (JViewport) parent;
2176 Dimension pref = getPreferredSize();
2177 if (viewport.getSize().width > pref.width)
2179 if ((getLayoutOrientation() == HORIZONTAL_WRAP)
2180 && (getVisibleRowCount() <= 0))
2187 * Gets the value of the </code>scrollableTracksViewportWidth</code> property.
2189 * @return <code>true</code> if the viewport is larger (vertically)
2190 * than the list and the list should be expanded to fit the viewport;
2191 * <code>false</code> if the viewport is smaller than the list and the
2192 * list should scroll (vertically) within the viewport
2194 public boolean getScrollableTracksViewportHeight()
2196 Component parent = getParent();
2197 boolean retVal = false;
2198 if (parent instanceof JViewport)
2200 JViewport viewport = (JViewport) parent;
2201 Dimension pref = getPreferredSize();
2202 if (viewport.getSize().height > pref.height)
2204 if ((getLayoutOrientation() == VERTICAL_WRAP)
2205 && (getVisibleRowCount() <= 0))
2212 * Returns the index of the anchor item in the current selection, or
2213 * <code>-1</code> if there is no anchor item.
2215 * @return The item index.
2217 public int getAnchorSelectionIndex()
2219 return selectionModel.getAnchorSelectionIndex();
2223 * Returns the index of the lead item in the current selection, or
2224 * <code>-1</code> if there is no lead item.
2226 * @return The item index.
2228 public int getLeadSelectionIndex()
2230 return selectionModel.getLeadSelectionIndex();
2234 * Returns the lowest item index in the current selection, or <code>-1</code>
2235 * if there is no selection.
2237 * @return The index.
2239 * @see #getMaxSelectionIndex()
2241 public int getMinSelectionIndex()
2243 return selectionModel.getMinSelectionIndex();
2247 * Returns the highest item index in the current selection, or
2248 * <code>-1</code> if there is no selection.
2250 * @return The index.
2252 * @see #getMinSelectionIndex()
2254 public int getMaxSelectionIndex()
2256 return selectionModel.getMaxSelectionIndex();
2260 * Clears the current selection.
2262 public void clearSelection()
2264 selectionModel.clearSelection();
2268 * Sets the current selection to the items in the specified range (inclusive).
2269 * Note that <code>anchor</code> can be less than, equal to, or greater than
2270 * <code>lead</code>.
2272 * @param anchor the index of the anchor item.
2273 * @param lead the index of the anchor item.
2275 public void setSelectionInterval(int anchor, int lead)
2277 selectionModel.setSelectionInterval(anchor, lead);
2281 * Adds the specified interval to the current selection. Note that
2282 * <code>anchor</code> can be less than, equal to, or greater than
2283 * <code>lead</code>.
2285 * @param anchor the index of the anchor item.
2286 * @param lead the index of the lead item.
2288 public void addSelectionInterval(int anchor, int lead)
2290 selectionModel.addSelectionInterval(anchor, lead);
2294 * Removes the specified interval from the current selection. Note that
2295 * <code>index0</code> can be less than, equal to, or greater than
2296 * <code>index1</code>.
2298 * @param index0 an index for one end of the range.
2299 * @param index1 an index for the other end of the range.
2301 public void removeSelectionInterval(int index0, int index1)
2303 selectionModel.removeSelectionInterval(index0, index1);
2307 * Returns the <code>valueIsAdjusting</code> flag from the list's selection
2312 public boolean getValueIsAdjusting()
2314 return selectionModel.getValueIsAdjusting();
2318 * Sets the <code>valueIsAdjusting</code> flag in the list's selection
2321 * @param isAdjusting the new value
2323 public void setValueIsAdjusting(boolean isAdjusting)
2325 selectionModel.setValueIsAdjusting(isAdjusting);
2329 * Return the value of the <code>dragEnabled</code> property.
2335 public boolean getDragEnabled()
2341 * Set the <code>dragEnabled</code> property.
2343 * @param enabled new value
2347 public void setDragEnabled(boolean enabled)
2349 dragEnabled = enabled;
2353 * Returns the layout orientation, which will be one of {@link #VERTICAL},
2354 * {@link #VERTICAL_WRAP} and {@link #HORIZONTAL_WRAP}. The default value
2355 * is {@link #VERTICAL}.
2357 * @return the orientation.
2359 * @see #setLayoutOrientation(int)
2362 public int getLayoutOrientation()
2364 return layoutOrientation;
2368 * Sets the layout orientation (this is a bound property with the name
2369 * 'layoutOrientation'). Valid orientations are {@link #VERTICAL},
2370 * {@link #VERTICAL_WRAP} and {@link #HORIZONTAL_WRAP}.
2372 * @param orientation the orientation.
2374 * @throws IllegalArgumentException if <code>orientation</code> is not one
2375 * of the specified values.
2377 * @see #getLayoutOrientation()
2379 public void setLayoutOrientation(int orientation)
2381 if (orientation < JList.VERTICAL || orientation > JList.HORIZONTAL_WRAP)
2382 throw new IllegalArgumentException();
2383 if (layoutOrientation == orientation)
2386 int old = layoutOrientation;
2387 layoutOrientation = orientation;
2388 firePropertyChange("layoutOrientation", old, orientation);
2392 * Returns the bounds of the rectangle that encloses both list cells
2393 * with index0 and index1.
2395 * @param index0 the index of the first cell
2396 * @param index1 the index of the second cell
2398 * @return the bounds of the rectangle that encloses both list cells
2399 * with index0 and index1, <code>null</code> if one of the indices is
2402 public Rectangle getCellBounds(int index0, int index1)
2404 ListUI ui = getUI();
2405 Rectangle bounds = null;
2408 bounds = ui.getCellBounds(this, index0, index1);
2410 // When the UI is null, this method also returns null in the RI.
2415 * Returns the index of the next list element (beginning at
2416 * <code>startIndex</code> and moving in the specified direction through the
2417 * list, looping around if necessary) that starts with <code>prefix</code>
2420 * @param prefix the prefix to search for in the cell values
2421 * @param startIndex the index where to start searching from
2422 * @param direction the search direction, either {@link Position.Bias#Forward}
2423 * or {@link Position.Bias#Backward} (<code>null</code> is interpreted
2424 * as {@link Position.Bias#Backward}.
2426 * @return the index of the found element or -1 if no such element has
2429 * @throws IllegalArgumentException if prefix is <code>null</code> or
2430 * startIndex is not valid
2434 public int getNextMatch(String prefix, int startIndex,
2435 Position.Bias direction)
2438 throw new IllegalArgumentException("The argument 'prefix' must not be"
2441 throw new IllegalArgumentException("The argument 'startIndex' must not"
2442 + " be less than zero.");
2444 int size = model.getSize();
2445 if (startIndex >= model.getSize())
2446 throw new IllegalArgumentException("The argument 'startIndex' must not"
2447 + " be greater than the number of"
2448 + " elements in the ListModel.");
2451 int current = startIndex;
2453 int itemCount = model.getSize();
2454 boolean finished = false;
2455 prefix = prefix.toUpperCase();
2457 if (direction == Position.Bias.Forward)
2461 String itemStr = model.getElementAt(current).toString().toUpperCase();
2462 if (itemStr.startsWith(prefix))
2464 current = (current + delta);
2466 current += itemCount;
2468 current = current % itemCount;
2469 finished = current == startIndex;
2475 * Returns a string describing the attributes for the <code>JList</code>
2476 * component, for use in debugging. The return value is guaranteed to be
2477 * non-<code>null</code>, but the format of the string may vary between
2480 * @return A string describing the attributes of the <code>JList</code>.
2482 protected String paramString()
2484 StringBuffer sb = new StringBuffer(super.paramString());
2485 sb.append(",fixedCellHeight=").append(getFixedCellHeight());
2486 sb.append(",fixedCellWidth=").append(getFixedCellWidth());
2487 sb.append(",selectionBackground=");
2488 if (getSelectionBackground() != null)
2489 sb.append(getSelectionBackground());
2490 sb.append(",selectionForeground=");
2491 if (getSelectionForeground() != null)
2492 sb.append(getSelectionForeground());
2493 sb.append(",visibleRowCount=").append(getVisibleRowCount());
2494 sb.append(",layoutOrientation=").append(getLayoutOrientation());
2495 return sb.toString();