| /* JList.java -- |
| Copyright (C) 2002, 2003, 2004, 2005, 2006, Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package javax.swing; |
| |
| import java.awt.Color; |
| import java.awt.Component; |
| import java.awt.ComponentOrientation; |
| import java.awt.Cursor; |
| import java.awt.Dimension; |
| import java.awt.Font; |
| import java.awt.FontMetrics; |
| import java.awt.Point; |
| import java.awt.Rectangle; |
| import java.awt.event.FocusListener; |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.util.Locale; |
| import java.util.Vector; |
| |
| import javax.accessibility.Accessible; |
| import javax.accessibility.AccessibleComponent; |
| import javax.accessibility.AccessibleContext; |
| import javax.accessibility.AccessibleRole; |
| import javax.accessibility.AccessibleSelection; |
| import javax.accessibility.AccessibleState; |
| import javax.accessibility.AccessibleStateSet; |
| import javax.swing.event.ListDataEvent; |
| import javax.swing.event.ListDataListener; |
| import javax.swing.event.ListSelectionEvent; |
| import javax.swing.event.ListSelectionListener; |
| import javax.swing.plaf.ListUI; |
| import javax.swing.text.Position; |
| |
| /** |
| * <p>This class is a facade over three separate objects: {@link |
| * javax.swing.ListModel}, {@link javax.swing.ListSelectionModel} and |
| * {@link javax.swing.plaf.ListUI}. The facade represents a unified "list" |
| * concept, with independently replacable (possibly client-provided) models |
| * for its contents and its current selection. In addition, each element in |
| * the list is rendered via a strategy class {@link |
| * javax.swing.ListCellRenderer}.</p> |
| * |
| * <p>Lists have many properties, some of which are stored in this class |
| * while others are delegated to the list's model or selection. The |
| * following properties are available:</p> |
| * |
| * <table> |
| * <tr><th>Property </th><th>Stored in</th><th>Bound?</th></tr> |
| * <tr><td>accessibleContext </td><td>list </td><td>no </td></tr> |
| * <tr><td>anchorSelectionIndex </td><td>selection</td><td>no </td></tr> |
| * <tr><td>cellRenderer </td><td>list </td><td>yes </td></tr> |
| * <tr><td>dragEnabled </td><td>list </td><td>no </td></tr> |
| * <tr><td>firstVisibleIndex </td><td>list </td><td>no </td></tr> |
| * <tr><td>fixedCellHeight </td><td>list </td><td>yes </td></tr> |
| * <tr><td>fixedCellWidth </td><td>list </td><td>yes </td></tr> |
| * <tr><td>lastVisibleIndex </td><td>list </td><td>no </td></tr> |
| * <tr><td>layoutOrientation </td><td>list </td><td>yes </td></tr> |
| * <tr><td>leadSelectionIndex </td><td>selection</td><td>no </td></tr> |
| * <tr><td>maxSelectionIndex </td><td>selection</td><td>no </td></tr> |
| * <tr><td>minSelectionIndex </td><td>selection</td><td>no </td></tr> |
| * <tr><td>model </td><td>list </td><td>yes </td></tr> |
| * <tr><td>opaque </td><td>list </td><td>no </td></tr> |
| * <tr><td>preferredScrollableViewportSize</td><td>list </td><td>no </td></tr> |
| * <tr><td>prototypeCellValue </td><td>list </td><td>yes </td></tr> |
| * <tr><td>scrollableTracksViewportHeight </td><td>list </td><td>no </td></tr> |
| * <tr><td>scrollableTracksViewportWidth </td><td>list </td><td>no </td></tr> |
| * <tr><td>selectedIndex </td><td>selection</td><td>no </td></tr> |
| * <tr><td>selectedIndices </td><td>selection</td><td>no </td></tr> |
| * <tr><td>selectedValue </td><td>model </td><td>no </td></tr> |
| * <tr><td>selectedValues </td><td>model </td><td>no </td></tr> |
| * <tr><td>selectionBackground </td><td>list </td><td>yes </td></tr> |
| * <tr><td>selectionEmpty </td><td>selection</td><td>no </td></tr> |
| * <tr><td>selectionForeground </td><td>list </td><td>yes </td></tr> |
| * <tr><td>selectionMode </td><td>selection</td><td>no </td></tr> |
| * <tr><td>selectionModel </td><td>list </td><td>yes </td></tr> |
| * <tr><td>UI </td><td>list </td><td>yes </td></tr> |
| * <tr><td>UIClassID </td><td>list </td><td>no </td></tr> |
| * <tr><td>valueIsAdjusting </td><td>list </td><td>no </td></tr> |
| * <tr><td>visibleRowCount </td><td>list </td><td>no </td></tr> |
| * </table> |
| * |
| * @author Graydon Hoare (graydon@redhat.com) |
| */ |
| |
| public class JList extends JComponent implements Accessible, Scrollable |
| { |
| |
| /** |
| * Provides accessibility support for <code>JList</code>. |
| */ |
| protected class AccessibleJList extends AccessibleJComponent |
| implements AccessibleSelection, PropertyChangeListener, |
| ListSelectionListener, ListDataListener |
| { |
| |
| /** |
| * Provides accessibility support for list elements in <code>JList</code>s. |
| */ |
| protected class AccessibleJListChild extends AccessibleContext |
| implements Accessible, AccessibleComponent |
| { |
| |
| /** |
| * The parent list. |
| */ |
| JList parent; |
| |
| /** |
| * The index in the list for that child. |
| */ |
| int listIndex; |
| |
| /** |
| * The cursor for this list child. |
| */ |
| // TODO: Testcases show that this class somehow stores state about the |
| // cursor. I cannot make up though how that could affect |
| // the actual list. |
| Cursor cursor = Cursor.getDefaultCursor(); |
| |
| /** |
| * Creates a new instance of <code>AccessibleJListChild</code>. |
| * |
| * @param list the list of which this is an accessible child |
| * @param index the list index for this child |
| */ |
| public AccessibleJListChild(JList list, int index) |
| { |
| parent = list; |
| listIndex = index; |
| } |
| |
| /** |
| * Returns the accessible context of this object. Returns |
| * <code>this</code> since <code>AccessibleJListChild</code>s are their |
| * own accessible contexts. |
| * |
| * @return the accessible context of this object, <code>this</code> |
| */ |
| public AccessibleContext getAccessibleContext() |
| { |
| return this; |
| } |
| |
| /** |
| * Returns the background color for this list child. This returns the |
| * background of the <code>JList</code> itself since the background |
| * cannot be set on list children individually |
| * |
| * @return the background color for this list child |
| */ |
| public Color getBackground() |
| { |
| return parent.getBackground(); |
| } |
| |
| /** |
| * Calling this method has no effect, since the background color cannot be |
| * set on list children individually. |
| * |
| * @param color not used here. |
| */ |
| public void setBackground(Color color) |
| { |
| // Calling this method has no effect, since the background color cannot |
| // be set on list children individually. |
| } |
| |
| /** |
| * Returns the foreground color for this list child. This returns the |
| * background of the <code>JList</code> itself since the foreground |
| * cannot be set on list children individually. |
| * |
| * @return the background color for this list child |
| */ |
| public Color getForeground() |
| { |
| return parent.getForeground(); |
| } |
| |
| /** |
| * Calling this method has no effect, since the foreground color cannot be |
| * set on list children individually. |
| * |
| * @param color not used here. |
| */ |
| public void setForeground(Color color) |
| { |
| // Calling this method has no effect, since the foreground color cannot |
| // be set on list children individually. |
| } |
| |
| /** |
| * Returns the cursor for this list child. |
| * |
| * @return the cursor for this list child |
| */ |
| public Cursor getCursor() |
| { |
| // TODO: Testcases show that this method returns the cursor that has |
| // been set by setCursor. I cannot make up though how that could affect |
| // the actual list. |
| return cursor; |
| } |
| |
| /** |
| * Sets the cursor for this list child. |
| */ |
| public void setCursor(Cursor cursor) |
| { |
| this.cursor = cursor; |
| // TODO: Testcases show that this method returns the cursor that has |
| // been set by setCursor. I cannot make up though how that could affect |
| // the actual list. |
| } |
| |
| /** |
| * Returns the font of the <code>JList</code> since it is not possible to |
| * set fonts for list children individually. |
| * |
| * @return the font of the <code>JList</code> |
| */ |
| public Font getFont() |
| { |
| return parent.getFont(); |
| } |
| |
| /** |
| * Does nothing since it is not possible to set the font on list children |
| * individually. |
| * |
| * @param font not used here |
| */ |
| public void setFont(Font font) |
| { |
| // Does nothing since it is not possible to set the font on list |
| // children individually. |
| } |
| |
| /** |
| * Returns the font metrics for the specified font. This method forwards |
| * to the parent <code>JList</code>. |
| * |
| * @param font the font for which the font metrics is queried |
| * |
| * @return the font metrics for the specified font |
| */ |
| public FontMetrics getFontMetrics(Font font) |
| { |
| return parent.getFontMetrics(font); |
| } |
| |
| /** |
| * Returns <code>true</code> if the parent <code>JList</code> is enabled, |
| * <code>false</code> otherwise. The list children cannot have an enabled |
| * flag set individually. |
| * |
| * @return <code>true</code> if the parent <code>JList</code> is enabled, |
| * <code>false</code> otherwise |
| */ |
| public boolean isEnabled() |
| { |
| return parent.isEnabled(); |
| } |
| |
| /** |
| * Does nothing since the enabled flag cannot be set for list children |
| * individually. |
| * |
| * @param b not used here |
| */ |
| public void setEnabled(boolean b) |
| { |
| // Does nothing since the enabled flag cannot be set for list children |
| // individually. |
| } |
| |
| /** |
| * Returns <code>true</code> if this list child is visible, |
| * <code>false</code> otherwise. The value of this property depends |
| * on {@link JList#getFirstVisibleIndex()} and |
| * {@link JList#getLastVisibleIndex()}. |
| * |
| * @return <code>true</code> if this list child is visible, |
| * <code>false</code> otherwise |
| */ |
| public boolean isVisible() |
| { |
| return listIndex >= parent.getFirstVisibleIndex() |
| && listIndex <= parent.getLastVisibleIndex(); |
| } |
| |
| /** |
| * The value of the visible property cannot be modified, so this method |
| * does nothing. |
| * |
| * @param b not used here |
| */ |
| public void setVisible(boolean b) |
| { |
| // The value of the visible property cannot be modified, so this method |
| // does nothing. |
| } |
| |
| /** |
| * Returns <code>true</code> if this list child is currently showing on |
| * screen and <code>false</code> otherwise. The list child is showing if |
| * it is visible and if it's parent JList is currently showing. |
| * |
| * @return <code>true</code> if this list child is currently showing on |
| * screen and <code>false</code> otherwise |
| */ |
| public boolean isShowing() |
| { |
| return isVisible() && parent.isShowing(); |
| } |
| |
| /** |
| * Returns <code>true</code> if this list child covers the screen location |
| * <code>point</code> (relative to the <code>JList</code> coordinate |
| * system, <code>false</code> otherwise. |
| * |
| * @return <code>true</code> if this list child covers the screen location |
| * <code>point</code> , <code>false</code> otherwise |
| */ |
| public boolean contains(Point point) |
| { |
| return getBounds().contains(point); |
| } |
| |
| /** |
| * Returns the absolute screen location of this list child. |
| * |
| * @return the absolute screen location of this list child |
| */ |
| public Point getLocationOnScreen() |
| { |
| Point loc = getLocation(); |
| SwingUtilities.convertPointToScreen(loc, parent); |
| return loc; |
| } |
| |
| /** |
| * Returns the screen location of this list child relative to it's parent. |
| * |
| * @return the location of this list child relative to it's parent |
| * |
| * @see JList#indexToLocation(int) |
| */ |
| public Point getLocation() |
| { |
| return parent.indexToLocation(listIndex); |
| } |
| |
| /** |
| * Does nothing since the screen location cannot be set on list children |
| * explictitly. |
| * |
| * @param point not used here |
| */ |
| public void setLocation(Point point) |
| { |
| // Does nothing since the screen location cannot be set on list children |
| // explictitly. |
| } |
| |
| /** |
| * Returns the bounds of this list child. |
| * |
| * @return the bounds of this list child |
| * |
| * @see JList#getCellBounds(int, int) |
| */ |
| public Rectangle getBounds() |
| { |
| return parent.getCellBounds(listIndex, listIndex); |
| } |
| |
| /** |
| * Does nothing since the bounds cannot be set on list children |
| * individually. |
| * |
| * @param rectangle not used here |
| */ |
| public void setBounds(Rectangle rectangle) |
| { |
| // Does nothing since the bounds cannot be set on list children |
| // individually. |
| } |
| |
| /** |
| * Returns the size of this list child. |
| * |
| * @return the size of this list child |
| */ |
| public Dimension getSize() |
| { |
| Rectangle b = getBounds(); |
| return b.getSize(); |
| } |
| |
| /** |
| * Does nothing since the size cannot be set on list children |
| * individually. |
| * |
| * @param dimension not used here |
| */ |
| public void setSize(Dimension dimension) |
| { |
| // Does nothing since the size cannot be set on list children |
| // individually. |
| } |
| |
| /** |
| * Returns <code>null</code> because list children do not have children |
| * themselves |
| * |
| * @return <code>null</code> |
| */ |
| public Accessible getAccessibleAt(Point point) |
| { |
| return null; |
| } |
| |
| /** |
| * Returns <code>true</code> since list children are focus traversable. |
| * |
| * @return true |
| */ |
| public boolean isFocusTraversable() |
| { |
| // TODO: Is this 100% ok? |
| return true; |
| } |
| |
| /** |
| * Requests focus on the parent list. List children cannot request focus |
| * individually. |
| */ |
| public void requestFocus() |
| { |
| // TODO: Is this 100% ok? |
| parent.requestFocus(); |
| } |
| |
| /** |
| * Adds a focus listener to the parent list. List children do not have |
| * their own focus management. |
| * |
| * @param listener the focus listener to add |
| */ |
| public void addFocusListener(FocusListener listener) |
| { |
| // TODO: Is this 100% ok? |
| parent.addFocusListener(listener); |
| } |
| |
| /** |
| * Removes a focus listener from the parent list. List children do not |
| * have their own focus management. |
| * |
| * @param listener the focus listener to remove |
| */ |
| public void removeFocusListener(FocusListener listener) |
| { |
| // TODO: Is this 100% |
| parent.removeFocusListener(listener); |
| } |
| |
| /** |
| * Returns the accessible role of this list item, which is |
| * {@link AccessibleRole#LABEL}. |
| * |
| * @return {@link AccessibleRole#LABEL} |
| */ |
| public AccessibleRole getAccessibleRole() |
| { |
| return AccessibleRole.LABEL; |
| } |
| |
| /** |
| * Returns the accessible state set of this list item. |
| * |
| * @return the accessible state set of this list item |
| */ |
| public AccessibleStateSet getAccessibleStateSet() |
| { |
| AccessibleStateSet states = new AccessibleStateSet(); |
| if (isVisible()) |
| states.add(AccessibleState.VISIBLE); |
| if (isShowing()) |
| states.add(AccessibleState.SHOWING); |
| if (isFocusTraversable()) |
| states.add(AccessibleState.FOCUSABLE); |
| // TODO: How should the active state be handled? The API docs |
| // suggest that this state is set on the activated list child, |
| // that is the one that is drawn with a box. However, I don't know how |
| // to implement this. |
| |
| // TODO: We set the selectable state here because list children are |
| // selectable. Is there a way to disable single children? |
| if (parent.isEnabled()) |
| states.add(AccessibleState.SELECTABLE); |
| |
| if (parent.isSelectedIndex(listIndex)) |
| states.add(AccessibleState.SELECTED); |
| |
| // TODO: Handle more states here? |
| return states; |
| } |
| |
| /** |
| * Returns the index of this list child within it's parent list. |
| * |
| * @return the index of this list child within it's parent list |
| */ |
| public int getAccessibleIndexInParent() |
| { |
| return listIndex; |
| } |
| |
| /** |
| * Returns <code>0</code> since list children don't have children |
| * themselves. |
| * |
| * @return <code>0</code> |
| */ |
| public int getAccessibleChildrenCount() |
| { |
| return 0; |
| } |
| |
| /** |
| * Returns <code>null</code> since list children don't have children |
| * themselves. |
| * |
| * @return <code>null</code> |
| */ |
| public Accessible getAccessibleChild(int i) |
| { |
| return null; |
| } |
| |
| /** |
| * Returns the locale of this component. This call is forwarded to the |
| * parent list since list children don't have a separate locale setting. |
| * |
| * @return the locale of this component |
| */ |
| public Locale getLocale() |
| { |
| return parent.getLocale(); |
| } |
| |
| /** |
| * This method does |
| * nothing, list children are transient accessible objects which means |
| * that they don't fire property change events. |
| * |
| * @param l not used here |
| */ |
| public void addPropertyChangeListener(PropertyChangeListener l) |
| { |
| // Do nothing here. |
| } |
| |
| /** |
| * This method does |
| * nothing, list children are transient accessible objects which means |
| * that they don't fire property change events. |
| * |
| * @param l not used here |
| */ |
| public void removePropertyChangeListener(PropertyChangeListener l) |
| { |
| // Do nothing here. |
| } |
| |
| // TODO: Implement the remaining methods of this class. |
| } |
| |
| /** |
| * Create a new AccessibleJList. |
| */ |
| public AccessibleJList() |
| { |
| // Nothing to do here. |
| } |
| |
| /** |
| * Returns the number of selected accessible children. |
| * |
| * @return the number of selected accessible children |
| */ |
| public int getAccessibleSelectionCount() |
| { |
| return getSelectedIndices().length; |
| } |
| |
| /** |
| * Returns the n-th selected accessible child. |
| * |
| * @param n the index of the selected child to return |
| * |
| * @return the n-th selected accessible child |
| */ |
| public Accessible getAccessibleSelection(int n) |
| { |
| return new AccessibleJListChild(JList.this, getSelectedIndices()[n]); |
| } |
| |
| /** |
| * Returns <code>true</code> if the n-th child is selected, |
| * <code>false</code> otherwise. |
| * |
| * @param n the index of the child of which the selected state is queried |
| * |
| * @return <code>true</code> if the n-th child is selected, |
| * <code>false</code> otherwise |
| */ |
| public boolean isAccessibleChildSelected(int n) |
| { |
| return isSelectedIndex(n); |
| } |
| |
| /** |
| * Adds the accessible item with the specified index to the selected items. |
| * If multiple selections are supported, the item is added to the selection, |
| * otherwise the item replaces the current selection. |
| * |
| * @param i the index of the item to add to the selection |
| */ |
| public void addAccessibleSelection(int i) |
| { |
| addSelectionInterval(i, i); |
| } |
| |
| /** |
| * Removes the accessible item with the specified index to the selection. |
| * |
| * @param i the index of the item to be removed from the selection |
| */ |
| public void removeAccessibleSelection(int i) |
| { |
| removeSelectionInterval(i, i); |
| } |
| |
| /** |
| * Remove all selection items from the selection. |
| */ |
| public void clearAccessibleSelection() |
| { |
| clearSelection(); |
| } |
| |
| /** |
| * Selects all items if multiple selections are supported. |
| * Otherwise do nothing. |
| */ |
| public void selectAllAccessibleSelection() |
| { |
| addSelectionInterval(0, getModel().getSize()); |
| } |
| |
| /** |
| * Receices notification when the list selection is changed. This method |
| * fires two property change events, the first with |
| * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY} and the second |
| * with {@link AccessibleContext#ACCESSIBLE_SELECTION_PROPERTY}. |
| * |
| * @param event the list selection event |
| */ |
| public void valueChanged(ListSelectionEvent event) |
| { |
| firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE, |
| Boolean.TRUE); |
| firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, Boolean.FALSE, |
| Boolean.TRUE); |
| } |
| |
| /** |
| * Receives notification when items have changed in the |
| * <code>JList</code>. This method fires a property change event with |
| * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}. |
| * |
| * @param event the list data event |
| */ |
| public void contentsChanged(ListDataEvent event) |
| { |
| firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE, |
| Boolean.TRUE); |
| } |
| |
| /** |
| * Receives notification when items are inserted into the |
| * <code>JList</code>. This method fires a property change event with |
| * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}. |
| * |
| * @param event the list data event |
| */ |
| public void intervalAdded(ListDataEvent event) |
| { |
| firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE, |
| Boolean.TRUE); |
| } |
| |
| /** |
| * Receives notification when items are removed from the |
| * <code>JList</code>. This method fires a property change event with |
| * {@link AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}. |
| * |
| * @param event the list data event |
| */ |
| public void intervalRemoved(ListDataEvent event) |
| { |
| firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE, |
| Boolean.TRUE); |
| } |
| |
| |
| /** |
| * Receives notification about changes of the <code>JList</code>'s |
| * properties. This is used to re-register this object as listener to |
| * the data model and selection model when the data model or selection model |
| * changes. |
| * |
| * @param e the property change event |
| */ |
| public void propertyChange(PropertyChangeEvent e) |
| { |
| String propertyName = e.getPropertyName(); |
| if (propertyName.equals("model")) |
| { |
| ListModel oldModel = (ListModel) e.getOldValue(); |
| oldModel.removeListDataListener(this); |
| ListModel newModel = (ListModel) e.getNewValue(); |
| newModel.addListDataListener(this); |
| } |
| else if (propertyName.equals("selectionModel")) |
| { |
| ListSelectionModel oldModel = (ListSelectionModel) e.getOldValue(); |
| oldModel.removeListSelectionListener(this); |
| ListSelectionModel newModel = (ListSelectionModel) e.getNewValue(); |
| oldModel.addListSelectionListener(this); |
| } |
| } |
| |
| /** |
| * Return the state set of the <code>JList</code>. |
| * |
| * @return the state set of the <code>JList</code> |
| */ |
| public AccessibleStateSet getAccessibleStateSet() |
| { |
| // TODO: Figure out if there is possibly more state that must be |
| // handled here. |
| AccessibleStateSet s = super.getAccessibleStateSet(); |
| if (getSelectionMode() != ListSelectionModel.SINGLE_SELECTION) |
| s.add(AccessibleState.MULTISELECTABLE); |
| return s; |
| } |
| |
| /** |
| * Returns the accessible role for <code>JList</code>, |
| * {@link AccessibleRole#LIST}. |
| * |
| * @return the accessible role for <code>JList</code> |
| */ |
| public AccessibleRole getAccessibleRole() |
| { |
| return AccessibleRole.LIST; |
| } |
| |
| /** |
| * Returns the accessible child at the visual location <code>p</code> |
| * (relative to the upper left corner of the <code>JList</code>). If there |
| * is no child at that location, this returns <code>null</code>. |
| * |
| * @param p the screen location for which to return the accessible child |
| * |
| * @return the accessible child at the specified location, or |
| * <code>null</code> if there is no child at that location |
| */ |
| public Accessible getAccessibleAt(Point p) |
| { |
| int childIndex = locationToIndex(p); |
| return getAccessibleChild(childIndex); |
| } |
| |
| /** |
| * Returns the number of accessible children in the <code>JList</code>. |
| * |
| * @return the number of accessible children in the <code>JList</code> |
| */ |
| public int getAccessibleChildrenCount() |
| { |
| return getModel().getSize(); |
| } |
| |
| /** |
| * Returns the n-th accessible child of this <code>JList</code>. This will |
| * be an instance of {@link AccessibleJListChild}. If there is no child |
| * at that index, <code>null</code> is returned. |
| * |
| * @param n the index of the child to return |
| * |
| * @return the n-th accessible child of this <code>JList</code> |
| */ |
| public Accessible getAccessibleChild(int n) |
| { |
| if (getModel().getSize() <= n) |
| return null; |
| return new AccessibleJListChild(JList.this, n); |
| } |
| } |
| |
| private static final long serialVersionUID = 4406629526391098046L; |
| |
| /** |
| * Constant value used in "layoutOrientation" property. This value means |
| * that cells are laid out in a single vertical column. This is the default. |
| */ |
| public static final int VERTICAL = 0; |
| |
| /** |
| * Constant value used in "layoutOrientation" property. This value means |
| * that cells are laid out in multiple columns "newspaper style", filling |
| * vertically first, then horizontally. |
| */ |
| public static final int VERTICAL_WRAP = 1; |
| |
| /** |
| * Constant value used in "layoutOrientation" property. This value means |
| * that cells are laid out in multiple columns "newspaper style", |
| * filling horizontally first, then vertically. |
| */ |
| public static final int HORIZONTAL_WRAP = 2; |
| |
| /** |
| * This property indicates whether "drag and drop" functions are enabled |
| * on the list. |
| */ |
| boolean dragEnabled; |
| |
| /** This property provides a strategy for rendering cells in the list. */ |
| ListCellRenderer cellRenderer; |
| |
| /** |
| * This property indicates an fixed width to assign to all cells in the |
| * list. If its value is <code>-1</code>, no width has been |
| * assigned. This value can be set explicitly, or implicitly by setting |
| * the {@link #prototypeCellValue} property. |
| */ |
| int fixedCellWidth; |
| |
| /** |
| * This property indicates an fixed height to assign to all cells in the |
| * list. If its value is <code>-1</code>, no height has been |
| * assigned. This value can be set explicitly, or implicitly by setting |
| * the {@link #prototypeCellValue} property. |
| */ |
| int fixedCellHeight; |
| |
| /** |
| * This property holds the current layout orientation of the list, which |
| * is one of the integer constants {@link #VERTICAL}, {@link |
| * #VERTICAL_WRAP}, or {@link #HORIZONTAL_WRAP}. |
| */ |
| int layoutOrientation; |
| |
| /** This property holds the data elements displayed by the list. */ |
| ListModel model; |
| |
| /** |
| * <p>This property holds a reference to a "prototype" data value -- |
| * typically a String -- which is used to calculate the {@link |
| * #fixedCellWidth} and {@link #fixedCellHeight} properties, using the |
| * {@link #cellRenderer} property to acquire a component to render the |
| * prototype.</p> |
| * |
| * <p>It is important that you <em>not</em> set this value to a |
| * component. It has to be a <em>data value</em> such as the objects you |
| * would find in the list's model. Setting it to a component will have |
| * undefined (and undesirable) affects. </p> |
| */ |
| Object prototypeCellValue; |
| |
| /** |
| * This property specifies a foreground color for the selected cells in |
| * the list. When {@link ListCellRenderer#getListCellRendererComponent} |
| * is called with a selected cell object, the component returned will |
| * have its "foreground" set to this color. |
| */ |
| Color selectionBackground; |
| |
| /** |
| * This property specifies a background color for the selected cells in |
| * the list. When {@link ListCellRenderer#getListCellRendererComponent} |
| * is called with a selected cell object, the component returned will |
| * have its "background" property set to this color. |
| */ |
| Color selectionForeground; |
| |
| /** |
| * This property holds a description of which data elements in the {@link |
| * #model} property should be considered "selected", when displaying and |
| * interacting with the list. |
| */ |
| ListSelectionModel selectionModel; |
| |
| /** |
| * This property indicates a <em>preference</em> for the number of rows |
| * displayed in the list, and will scale the |
| * {@link #getPreferredScrollableViewportSize} property accordingly. The actual |
| * number of displayed rows, when the list is placed in a real {@link |
| * JViewport} or other component, may be greater or less than this number. |
| */ |
| int visibleRowCount; |
| |
| /** |
| * Fire a {@link ListSelectionEvent} to all the registered |
| * ListSelectionListeners. |
| * |
| * @param firstIndex the lowest index covering the selection change. |
| * @param lastIndex the highest index covering the selection change. |
| * @param isAdjusting a flag indicating if this event is one in a series |
| * of events updating the selection. |
| */ |
| protected void fireSelectionValueChanged(int firstIndex, int lastIndex, |
| boolean isAdjusting) |
| { |
| ListSelectionEvent evt = new ListSelectionEvent(this, firstIndex, |
| lastIndex, isAdjusting); |
| ListSelectionListener listeners[] = getListSelectionListeners(); |
| for (int i = 0; i < listeners.length; ++i) |
| { |
| listeners[i].valueChanged(evt); |
| } |
| } |
| |
| /** |
| * This private listener propagates {@link ListSelectionEvent} events |
| * from the list's "selectionModel" property to the list's {@link |
| * ListSelectionListener} listeners. It also listens to {@link |
| * ListDataEvent} events from the list's {@link #model} property. If this |
| * class receives either type of event, it triggers repainting of the |
| * list. |
| */ |
| private class ListListener |
| implements ListSelectionListener, ListDataListener |
| { |
| // ListDataListener events |
| public void contentsChanged(ListDataEvent event) |
| { |
| JList.this.revalidate(); |
| JList.this.repaint(); |
| } |
| public void intervalAdded(ListDataEvent event) |
| { |
| JList.this.revalidate(); |
| JList.this.repaint(); |
| } |
| public void intervalRemoved(ListDataEvent event) |
| { |
| JList.this.revalidate(); |
| JList.this.repaint(); |
| } |
| // ListSelectionListener events |
| public void valueChanged(ListSelectionEvent event) |
| { |
| JList.this.fireSelectionValueChanged(event.getFirstIndex(), |
| event.getLastIndex(), |
| event.getValueIsAdjusting()); |
| JList.this.repaint(); |
| } |
| } |
| |
| /** |
| * Shared ListListener instance, subscribed to both the current {@link |
| * #model} and {@link #selectionModel} properties of the list. |
| */ |
| ListListener listListener; |
| |
| |
| /** |
| * Creates a new <code>JList</code> object. |
| */ |
| public JList() |
| { |
| init(new DefaultListModel()); |
| } |
| |
| /** |
| * Creates a new <code>JList</code> object. |
| * |
| * @param items the initial list items. |
| */ |
| public JList(Object[] items) |
| { |
| init(createListModel(items)); |
| } |
| |
| /** |
| * Creates a new <code>JList</code> object. |
| * |
| * @param items the initial list items. |
| */ |
| public JList(Vector items) |
| { |
| init(createListModel(items)); |
| } |
| |
| /** |
| * Creates a new <code>JList</code> object. |
| * |
| * @param model a model containing the list items (<code>null</code> not |
| * permitted). |
| * |
| * @throws IllegalArgumentException if <code>model</code> is |
| * <code>null</code>. |
| */ |
| public JList(ListModel model) |
| { |
| init(model); |
| } |
| |
| /** |
| * Initializes the list. |
| * |
| * @param m the list model (<code>null</code> not permitted). |
| */ |
| private void init(ListModel m) |
| { |
| if (m == null) |
| throw new IllegalArgumentException("Null model not permitted."); |
| dragEnabled = false; |
| fixedCellHeight = -1; |
| fixedCellWidth = -1; |
| layoutOrientation = VERTICAL; |
| opaque = true; |
| visibleRowCount = 8; |
| |
| cellRenderer = new DefaultListCellRenderer(); |
| listListener = new ListListener(); |
| |
| model = m; |
| if (model != null) |
| model.addListDataListener(listListener); |
| |
| selectionModel = createSelectionModel(); |
| if (selectionModel != null) |
| { |
| selectionModel.addListSelectionListener(listListener); |
| selectionModel.setSelectionMode |
| (ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); |
| } |
| setLayout(null); |
| |
| updateUI(); |
| } |
| |
| /** |
| * Creates the default <code>ListSelectionModel</code>. |
| * |
| * @return the <code>ListSelectionModel</code> |
| */ |
| protected ListSelectionModel createSelectionModel() |
| { |
| return new DefaultListSelectionModel(); |
| } |
| |
| /** |
| * Gets the value of the {@link #fixedCellHeight} property. This property |
| * may be <code>-1</code> to indicate that no cell height has been |
| * set. This property is also set implicitly when the |
| * {@link #prototypeCellValue} property is set. |
| * |
| * @return The current value of the property |
| * |
| * @see #fixedCellHeight |
| * @see #setFixedCellHeight |
| * @see #setPrototypeCellValue |
| */ |
| public int getFixedCellHeight() |
| { |
| return fixedCellHeight; |
| } |
| |
| /** |
| * Sets the value of the {@link #fixedCellHeight} property. This property |
| * may be <code>-1</code> to indicate that no cell height has been |
| * set. This property is also set implicitly when the {@link |
| * #prototypeCellValue} property is set, but setting it explicitly |
| * overrides the height computed from {@link #prototypeCellValue}. |
| * |
| * @param h the height. |
| * |
| * @see #getFixedCellHeight |
| * @see #getPrototypeCellValue |
| */ |
| public void setFixedCellHeight(int h) |
| { |
| if (fixedCellHeight == h) |
| return; |
| |
| int old = fixedCellHeight; |
| fixedCellHeight = h; |
| firePropertyChange("fixedCellHeight", old, h); |
| } |
| |
| |
| /** |
| * Gets the value of the {@link #fixedCellWidth} property. This property |
| * may be <code>-1</code> to indicate that no cell width has been |
| * set. This property is also set implicitly when the {@link |
| * #prototypeCellValue} property is set. |
| * |
| * @return The current value of the property |
| * |
| * @see #setFixedCellWidth |
| * @see #setPrototypeCellValue |
| */ |
| public int getFixedCellWidth() |
| { |
| return fixedCellWidth; |
| } |
| |
| /** |
| * Sets the value of the {@link #fixedCellWidth} property. This property |
| * may be <code>-1</code> to indicate that no cell width has been |
| * set. This property is also set implicitly when the {@link |
| * #prototypeCellValue} property is set, but setting it explicitly |
| * overrides the width computed from {@link #prototypeCellValue}. |
| * |
| * @param w the width. |
| * |
| * @see #getFixedCellHeight |
| * @see #getPrototypeCellValue |
| */ |
| public void setFixedCellWidth(int w) |
| { |
| if (fixedCellWidth == w) |
| return; |
| |
| int old = fixedCellWidth; |
| fixedCellWidth = w; |
| firePropertyChange("fixedCellWidth", old, w); |
| } |
| |
| /** |
| * Gets the value of the {@link #visibleRowCount} property. The default |
| * value is 8. |
| * |
| * @return the current value of the property. |
| * |
| * @see #setVisibleRowCount(int) |
| */ |
| public int getVisibleRowCount() |
| { |
| return visibleRowCount; |
| } |
| |
| /** |
| * Sets the value of the {@link #visibleRowCount} property. |
| * |
| * @param vc The new property value |
| * |
| * @see #getVisibleRowCount() |
| */ |
| public void setVisibleRowCount(int vc) |
| { |
| if (visibleRowCount != vc) |
| { |
| int oldValue = visibleRowCount; |
| visibleRowCount = Math.max(vc, 0); |
| firePropertyChange("visibleRowCount", oldValue, vc); |
| revalidate(); |
| repaint(); |
| } |
| } |
| |
| /** |
| * Adds a {@link ListSelectionListener} to the listener list for this |
| * list. The listener will be called back with a {@link |
| * ListSelectionEvent} any time the list's {@link #selectionModel} |
| * property changes. The source of such events will be the JList, |
| * not the selection model. |
| * |
| * @param listener The new listener to add |
| */ |
| public void addListSelectionListener(ListSelectionListener listener) |
| { |
| listenerList.add (ListSelectionListener.class, listener); |
| } |
| |
| /** |
| * Removes a {@link ListSelectionListener} from the listener list for |
| * this list. The listener will no longer be called when the list's |
| * {@link #selectionModel} changes. |
| * |
| * @param listener The listener to remove |
| */ |
| public void removeListSelectionListener(ListSelectionListener listener) |
| { |
| listenerList.remove(ListSelectionListener.class, listener); |
| } |
| |
| /** |
| * Returns an array of all ListSelectionListeners subscribed to this |
| * list. |
| * |
| * @return The current subscribed listeners |
| * |
| * @since 1.4 |
| */ |
| public ListSelectionListener[] getListSelectionListeners() |
| { |
| return (ListSelectionListener[]) getListeners(ListSelectionListener.class); |
| } |
| |
| /** |
| * Returns the selection mode for the list (one of: |
| * {@link ListSelectionModel#SINGLE_SELECTION}, |
| * {@link ListSelectionModel#SINGLE_INTERVAL_SELECTION} and |
| * {@link ListSelectionModel#MULTIPLE_INTERVAL_SELECTION}). |
| * |
| * @return The selection mode. |
| * |
| * @see #setSelectionMode(int) |
| */ |
| public int getSelectionMode() |
| { |
| return selectionModel.getSelectionMode(); |
| } |
| |
| /** |
| * Sets the list's "selectionMode" property, which simply mirrors the |
| * same property on the list's {@link #selectionModel} property. This |
| * property should be one of the integer constants |
| * <code>SINGLE_SELECTION</code>, <code>SINGLE_INTERVAL_SELECTION</code>, |
| * or <code>MULTIPLE_INTERVAL_SELECTION</code> from the {@link |
| * ListSelectionModel} interface. |
| * |
| * @param a The new selection mode |
| */ |
| public void setSelectionMode(int a) |
| { |
| selectionModel.setSelectionMode(a); |
| } |
| |
| /** |
| * Adds the interval <code>[a,a]</code> to the set of selections managed |
| * by this list's {@link #selectionModel} property. Depending on the |
| * selection mode, this may cause existing selections to become invalid, |
| * or may simply expand the set of selections. |
| * |
| * @param a A number in the half-open range <code>[0, x)</code> where |
| * <code>x = getModel.getSize()</code>, indicating the index of an |
| * element in the list to select. When < 0 the selection is cleared. |
| * |
| * @see #setSelectionMode |
| * @see #selectionModel |
| */ |
| public void setSelectedIndex(int a) |
| { |
| if (a < 0) |
| selectionModel.clearSelection(); |
| else |
| selectionModel.setSelectionInterval(a, a); |
| } |
| |
| /** |
| * For each element <code>a[i]</code> of the provided array |
| * <code>a</code>, calls {@link #setSelectedIndex} on <code>a[i]</code>. |
| * |
| * @param a an array of selected indices (<code>null</code> not permitted). |
| * |
| * @throws NullPointerException if <code>a</code> is <code>null</code>. |
| * @see #setSelectionMode |
| * @see #selectionModel |
| */ |
| public void setSelectedIndices(int [] a) |
| { |
| for (int i = 0; i < a.length; ++i) |
| setSelectedIndex(a[i]); |
| } |
| |
| /** |
| * Returns the minimum index of an element in the list which is currently |
| * selected. |
| * |
| * @return A number in the half-open range <code>[0, x)</code> where |
| * <code>x = getModel.getSize()</code>, indicating the minimum index of |
| * an element in the list for which the element is selected, or |
| * <code>-1</code> if no elements are selected |
| */ |
| public int getSelectedIndex() |
| { |
| return selectionModel.getMinSelectionIndex(); |
| } |
| |
| /** |
| * Returns <code>true</code> if the model's selection is empty, otherwise |
| * <code>false</code>. |
| * |
| * @return The return value of {@link ListSelectionModel#isSelectionEmpty} |
| */ |
| public boolean isSelectionEmpty() |
| { |
| return selectionModel.isSelectionEmpty(); |
| } |
| |
| /** |
| * Returns the list index of the upper left or upper right corner of the |
| * visible rectangle of this list, depending on the {@link |
| * Component#getComponentOrientation} property. |
| * |
| * @return The index of the first visible list cell, or <code>-1</code> |
| * if none is visible. |
| */ |
| public int getFirstVisibleIndex() |
| { |
| ComponentOrientation or = getComponentOrientation(); |
| Rectangle r = getVisibleRect(); |
| if (or == ComponentOrientation.RIGHT_TO_LEFT) |
| r.translate((int) r.getWidth() - 1, 0); |
| return getUI().locationToIndex(this, r.getLocation()); |
| } |
| |
| |
| /** |
| * Returns index of the cell to which specified location is closest to. If |
| * the location is outside the bounds of the list, then the greatest index |
| * in the list model is returned. If the list model is empty, then |
| * <code>-1</code> is returned. |
| * |
| * @param location for which to look for in the list |
| * |
| * @return index of the cell to which specified location is closest to. |
| */ |
| public int locationToIndex(Point location) |
| { |
| return getUI().locationToIndex(this, location); |
| } |
| |
| /** |
| * Returns location of the cell located at the specified index in the list. |
| * @param index of the cell for which location will be determined |
| * |
| * @return location of the cell located at the specified index in the list. |
| */ |
| public Point indexToLocation(int index) |
| { |
| return getUI().indexToLocation(this, index); |
| } |
| |
| /** |
| * Returns the list index of the lower right or lower left corner of the |
| * visible rectangle of this list, depending on the {@link |
| * Component#getComponentOrientation} property. |
| * |
| * @return The index of the last visible list cell, or <code>-1</code> |
| * if none is visible. |
| */ |
| public int getLastVisibleIndex() |
| { |
| ComponentOrientation or = getComponentOrientation(); |
| Rectangle r = getVisibleRect(); |
| r.translate(0, (int) r.getHeight() - 1); |
| if (or == ComponentOrientation.LEFT_TO_RIGHT) |
| r.translate((int) r.getWidth() - 1, 0); |
| if (getUI().locationToIndex(this, r.getLocation()) == -1 |
| && indexToLocation(getModel().getSize() - 1).y < r.y) |
| return getModel().getSize() - 1; |
| return getUI().locationToIndex(this, r.getLocation()); |
| } |
| |
| /** |
| * Returns the indices of values in the {@link #model} property which are |
| * selected. |
| * |
| * @return An array of model indices, each of which is selected according |
| * to the {@link #getSelectedValues} property |
| */ |
| public int[] getSelectedIndices() |
| { |
| int lo, hi, n, i, j; |
| if (selectionModel.isSelectionEmpty()) |
| return new int[0]; |
| lo = selectionModel.getMinSelectionIndex(); |
| hi = selectionModel.getMaxSelectionIndex(); |
| n = 0; |
| for (i = lo; i <= hi; ++i) |
| if (selectionModel.isSelectedIndex(i)) |
| n++; |
| int [] v = new int[n]; |
| j = 0; |
| for (i = lo; i <= hi; ++i) |
| if (selectionModel.isSelectedIndex(i)) |
| v[j++] = i; |
| return v; |
| } |
| |
| /** |
| * Indicates whether the list element at a given index value is |
| * currently selected. |
| * |
| * @param a The index to check |
| * @return <code>true</code> if <code>a</code> is the index of a selected |
| * list element |
| */ |
| public boolean isSelectedIndex(int a) |
| { |
| return selectionModel.isSelectedIndex(a); |
| } |
| |
| /** |
| * Returns the first value in the list's {@link #model} property which is |
| * selected, according to the list's {@link #selectionModel} property. |
| * This is equivalent to calling |
| * <code>getModel()getElementAt(getSelectedIndex())</code>, with a check |
| * for the special index value of <code>-1</code> which returns null |
| * <code>null</code>. |
| * |
| * @return The first selected element, or <code>null</code> if no element |
| * is selected. |
| * |
| * @see #getSelectedValues |
| */ |
| public Object getSelectedValue() |
| { |
| int index = getSelectedIndex(); |
| if (index == -1) |
| return null; |
| return getModel().getElementAt(index); |
| } |
| |
| /** |
| * Returns all the values in the list's {@link #model} property which are |
| * selected, according to the list's {@link #selectionModel} property. |
| * |
| * @return An array containing all the selected values |
| * @see #setSelectedValue |
| */ |
| public Object[] getSelectedValues() |
| { |
| int[] idx = getSelectedIndices(); |
| Object[] v = new Object[idx.length]; |
| for (int i = 0; i < idx.length; ++i) |
| v[i] = getModel().getElementAt(idx[i]); |
| return v; |
| } |
| |
| /** |
| * Gets the value of the {@link #selectionBackground} property. |
| * |
| * @return The current value of the property |
| */ |
| public Color getSelectionBackground() |
| { |
| return selectionBackground; |
| } |
| |
| /** |
| * Sets the value of the {@link #selectionBackground} property. |
| * |
| * @param c The new value of the property |
| */ |
| public void setSelectionBackground(Color c) |
| { |
| if (selectionBackground == c) |
| return; |
| |
| Color old = selectionBackground; |
| selectionBackground = c; |
| firePropertyChange("selectionBackground", old, c); |
| repaint(); |
| } |
| |
| /** |
| * Gets the value of the {@link #selectionForeground} property. |
| * |
| * @return The current value of the property |
| */ |
| public Color getSelectionForeground() |
| { |
| return selectionForeground; |
| } |
| |
| /** |
| * Sets the value of the {@link #selectionForeground} property. |
| * |
| * @param c The new value of the property |
| */ |
| public void setSelectionForeground(Color c) |
| { |
| if (selectionForeground == c) |
| return; |
| |
| Color old = selectionForeground; |
| selectionForeground = c; |
| firePropertyChange("selectionForeground", old, c); |
| } |
| |
| /** |
| * Sets the selection to cover only the specified value, if it |
| * exists in the model. |
| * |
| * @param obj The object to select |
| * @param scroll Whether to scroll the list to make the newly selected |
| * value visible |
| * |
| * @see #ensureIndexIsVisible |
| */ |
| |
| public void setSelectedValue(Object obj, boolean scroll) |
| { |
| for (int i = 0; i < model.getSize(); ++i) |
| { |
| if (model.getElementAt(i).equals(obj)) |
| { |
| setSelectedIndex(i); |
| if (scroll) |
| ensureIndexIsVisible(i); |
| break; |
| } |
| } |
| } |
| |
| /** |
| * Scrolls this list to make the specified cell visible. This |
| * only works if the list is contained within a viewport. |
| * |
| * @param i The list index to make visible |
| * |
| * @see JComponent#scrollRectToVisible |
| */ |
| public void ensureIndexIsVisible(int i) |
| { |
| Rectangle r = getUI().getCellBounds(this, i, i); |
| if (r != null) |
| scrollRectToVisible(r); |
| } |
| |
| /** |
| * Sets the {@link #model} property of the list to a new anonymous |
| * {@link AbstractListModel} subclass which accesses the provided Object |
| * array directly. |
| * |
| * @param listData The object array to build a new list model on |
| * @see #setModel |
| */ |
| public void setListData(Object[] listData) |
| { |
| setModel(createListModel(listData)); |
| } |
| |
| /** |
| * Returns a {@link ListModel} backed by the specified array. |
| * |
| * @param items the list items (don't use <code>null</code>). |
| * |
| * @return A list model containing the specified items. |
| */ |
| private ListModel createListModel(final Object[] items) |
| { |
| return new AbstractListModel() |
| { |
| public int getSize() |
| { |
| return items.length; |
| } |
| public Object getElementAt(int i) |
| { |
| return items[i]; |
| } |
| }; |
| } |
| |
| /** |
| * Returns a {@link ListModel} backed by the specified vector. |
| * |
| * @param items the list items (don't use <code>null</code>). |
| * |
| * @return A list model containing the specified items. |
| */ |
| private ListModel createListModel(final Vector items) |
| { |
| return new AbstractListModel() |
| { |
| public int getSize() |
| { |
| return items.size(); |
| } |
| public Object getElementAt(int i) |
| { |
| return items.get(i); |
| } |
| }; |
| } |
| |
| /** |
| * Sets the {@link #model} property of the list to a new anonymous {@link |
| * AbstractListModel} subclass which accesses the provided vector |
| * directly. |
| * |
| * @param listData The object array to build a new list model on |
| * @see #setModel |
| */ |
| public void setListData(Vector listData) |
| { |
| setModel(createListModel(listData)); |
| } |
| |
| /** |
| * Gets the value of the {@link #cellRenderer} property. |
| * |
| * @return The current value of the property |
| */ |
| public ListCellRenderer getCellRenderer() |
| { |
| return cellRenderer; |
| } |
| |
| /** |
| * Sets the value of the {@link #getCellRenderer} property. |
| * |
| * @param renderer The new property value |
| */ |
| public void setCellRenderer(ListCellRenderer renderer) |
| { |
| if (cellRenderer == renderer) |
| return; |
| |
| ListCellRenderer old = cellRenderer; |
| cellRenderer = renderer; |
| firePropertyChange("cellRenderer", old, renderer); |
| revalidate(); |
| repaint(); |
| } |
| |
| /** |
| * Gets the value of the {@link #model} property. |
| * |
| * @return The current value of the property |
| */ |
| public ListModel getModel() |
| { |
| return model; |
| } |
| |
| /** |
| * Sets the value of the {@link #model} property. The list's {@link |
| * #listListener} is unsubscribed from the existing model, if it exists, |
| * and re-subscribed to the new model. |
| * |
| * @param model the new model (<code>null</code> not permitted). |
| * |
| * @throws IllegalArgumentException if <code>model</code> is |
| * <code>null</code>. |
| */ |
| public void setModel(ListModel model) |
| { |
| if (model == null) |
| throw new IllegalArgumentException("Null 'model' argument."); |
| if (this.model == model) |
| return; |
| |
| if (this.model != null) |
| this.model.removeListDataListener(listListener); |
| |
| ListModel old = this.model; |
| this.model = model; |
| |
| if (this.model != null) |
| this.model.addListDataListener(listListener); |
| |
| firePropertyChange("model", old, model); |
| revalidate(); |
| repaint(); |
| } |
| |
| /** |
| * Returns the selection model for the {@link JList} component. Note that |
| * this class contains a range of convenience methods for configuring the |
| * selection model:<br> |
| * <ul> |
| * <li>{@link #clearSelection()};</li> |
| * <li>{@link #setSelectionMode(int)};</li> |
| * <li>{@link #addSelectionInterval(int, int)};</li> |
| * <li>{@link #setSelectedIndex(int)};</li> |
| * <li>{@link #setSelectedIndices(int[])};</li> |
| * <li>{@link #setSelectionInterval(int, int)}.</li> |
| * </ul> |
| * |
| * @return The selection model. |
| */ |
| public ListSelectionModel getSelectionModel() |
| { |
| return selectionModel; |
| } |
| |
| /** |
| * Sets the value of the {@link #selectionModel} property. The list's |
| * {@link #listListener} is unsubscribed from the existing selection |
| * model, if it exists, and re-subscribed to the new selection model. |
| * |
| * @param model The new property value |
| */ |
| public void setSelectionModel(ListSelectionModel model) |
| { |
| if (selectionModel == model) |
| return; |
| |
| if (selectionModel != null) |
| selectionModel.removeListSelectionListener(listListener); |
| |
| ListSelectionModel old = selectionModel; |
| selectionModel = model; |
| |
| if (selectionModel != null) |
| selectionModel.addListSelectionListener(listListener); |
| |
| firePropertyChange("selectionModel", old, model); |
| revalidate(); |
| repaint(); |
| } |
| |
| /** |
| * Gets the value of the UI property. |
| * |
| * @return The current property value |
| */ |
| public ListUI getUI() |
| { |
| return (ListUI) ui; |
| } |
| |
| /** |
| * Sets the value of the UI property. |
| * |
| * @param ui The new property value |
| */ |
| public void setUI(ListUI ui) |
| { |
| super.setUI(ui); |
| } |
| |
| /** |
| * Calls {@link #setUI} with the {@link ListUI} subclass |
| * returned from calling {@link UIManager#getUI}. |
| */ |
| public void updateUI() |
| { |
| setUI((ListUI) UIManager.getUI(this)); |
| } |
| |
| /** |
| * Return the class identifier for the list's UI property. This should |
| * be the constant string <code>"ListUI"</code>, and map to an |
| * appropriate UI class in the {@link UIManager}. |
| * |
| * @return The class identifier |
| */ |
| public String getUIClassID() |
| { |
| return "ListUI"; |
| } |
| |
| |
| /** |
| * Returns the current value of the {@link #prototypeCellValue} |
| * property. This property holds a reference to a "prototype" data value |
| * -- typically a String -- which is used to calculate the {@link |
| * #fixedCellWidth} and {@link #fixedCellHeight} properties, using the |
| * {@link #cellRenderer} property to acquire a component to render the |
| * prototype. |
| * |
| * @return The current prototype cell value |
| * @see #setPrototypeCellValue |
| */ |
| public Object getPrototypeCellValue() |
| { |
| return prototypeCellValue; |
| } |
| |
| /** |
| * <p>Set the {@link #prototypeCellValue} property. This property holds a |
| * reference to a "prototype" data value -- typically a String -- which |
| * is used to calculate the {@link #fixedCellWidth} and {@link |
| * #fixedCellHeight} properties, using the {@link #cellRenderer} property |
| * to acquire a component to render the prototype.</p> |
| * |
| * <p>It is important that you <em>not</em> set this value to a |
| * component. It has to be a <em>data value</em> such as the objects you |
| * would find in the list's model. Setting it to a component will have |
| * undefined (and undesirable) affects. </p> |
| * |
| * @param obj The new prototype cell value |
| * @see #getPrototypeCellValue |
| */ |
| public void setPrototypeCellValue(Object obj) |
| { |
| if (prototypeCellValue == obj) |
| return; |
| |
| Object old = prototypeCellValue; |
| Component comp = getCellRenderer() |
| .getListCellRendererComponent(this, obj, 0, false, false); |
| Dimension d = comp.getPreferredSize(); |
| fixedCellWidth = d.width; |
| fixedCellHeight = d.height; |
| prototypeCellValue = obj; |
| firePropertyChange("prototypeCellValue", old, obj); |
| } |
| |
| public AccessibleContext getAccessibleContext() |
| { |
| return new AccessibleJList(); |
| } |
| |
| /** |
| * Returns a size indicating how much space this list would like to |
| * consume, when contained in a scrollable viewport. This is part of the |
| * {@link Scrollable} interface, which interacts with {@link |
| * ScrollPaneLayout} and {@link JViewport} to define scrollable objects. |
| * |
| * @return The preferred size |
| */ |
| public Dimension getPreferredScrollableViewportSize() |
| { |
| //If the layout orientation is not VERTICAL, then this will |
| //return the value from getPreferredSize. The current ListUI is |
| //expected to override getPreferredSize to return an appropriate value. |
| if (getLayoutOrientation() != VERTICAL) |
| return getPreferredSize(); |
| |
| int size = getModel().getSize(); |
| |
| // Trivial case: if fixedCellWidth and fixedCellHeight were set |
| // just use them |
| if (fixedCellHeight != -1 && fixedCellWidth != -1) |
| return new Dimension(fixedCellWidth, size * fixedCellHeight); |
| |
| // If the model is empty we use 16 * the number of visible rows |
| // for the height and either fixedCellWidth (if set) or 256 |
| // for the width |
| if (size == 0) |
| { |
| if (fixedCellWidth == -1) |
| return new Dimension(256, 16 * getVisibleRowCount()); |
| else |
| return new Dimension(fixedCellWidth, 16 * getVisibleRowCount()); |
| } |
| |
| // Calculate the width: if fixedCellWidth was set use that, otherwise |
| // use the preferredWidth |
| int prefWidth; |
| if (fixedCellWidth != -1) |
| prefWidth = fixedCellWidth; |
| else |
| prefWidth = getPreferredSize().width; |
| |
| // Calculate the height: if fixedCellHeight was set use that, otherwise |
| // use the height of the first row multiplied by the number of visible |
| // rows |
| int prefHeight; |
| if (fixedCellHeight != -1) |
| prefHeight = fixedCellHeight; |
| else |
| prefHeight = getVisibleRowCount() * getCellBounds(0, 0).height; |
| |
| return new Dimension (prefWidth, prefHeight); |
| } |
| |
| /** |
| * <p>Return the number of pixels the list must scroll in order to move a |
| * "unit" of the list into the provided visible rectangle. When the |
| * provided direction is positive, the call describes a "downwards" |
| * scroll, which will be exposing a cell at a <em>greater</em> index in |
| * the list than those elements currently showing. Then the provided |
| * direction is negative, the call describes an "upwards" scroll, which |
| * will be exposing a cell at a <em>lesser</em> index in the list than |
| * those elements currently showing.</p> |
| * |
| * <p>If the provided orientation is <code>HORIZONTAL</code>, the above |
| * comments refer to "rightwards" for positive direction, and "leftwards" |
| * for negative.</p> |
| * |
| * |
| * @param visibleRect The rectangle to scroll an element into |
| * @param orientation One of the numeric consants <code>VERTICAL</code> |
| * or <code>HORIZONTAL</code> |
| * @param direction An integer indicating the scroll direction: positive means |
| * forwards (down, right), negative means backwards (up, left) |
| * |
| * @return The scrollable unit increment, in pixels |
| */ |
| public int getScrollableUnitIncrement(Rectangle visibleRect, |
| int orientation, int direction) |
| { |
| ListUI lui = this.getUI(); |
| if (orientation == SwingConstants.VERTICAL) |
| { |
| if (direction > 0) |
| { |
| // Scrolling down |
| Point bottomLeft = new Point(visibleRect.x, |
| visibleRect.y + visibleRect.height); |
| int curIdx = lui.locationToIndex(this, bottomLeft); |
| Rectangle curBounds = lui.getCellBounds(this, curIdx, curIdx); |
| if (curBounds.y + curBounds.height == bottomLeft.y) |
| { |
| // we are at the exact bottom of the current cell, so we |
| // are being asked to scroll to the end of the next one |
| if (curIdx + 1 < model.getSize()) |
| { |
| // there *is* a next item in the list |
| Rectangle nxtBounds = lui.getCellBounds(this, curIdx + 1, curIdx + 1); |
| return nxtBounds.height; |
| } |
| else |
| { |
| // no next item, no advance possible |
| return 0; |
| } |
| } |
| else |
| { |
| // we are part way through an existing cell, so we are being |
| // asked to scroll to the bottom of it |
| return (curBounds.y + curBounds.height) - bottomLeft.y; |
| } |
| } |
| else |
| { |
| // scrolling up |
| Point topLeft = new Point(visibleRect.x, visibleRect.y); |
| int curIdx = lui.locationToIndex(this, topLeft); |
| Rectangle curBounds = lui.getCellBounds(this, curIdx, curIdx); |
| if (curBounds.y == topLeft.y) |
| { |
| // we are at the exact top of the current cell, so we |
| // are being asked to scroll to the top of the previous one |
| if (curIdx > 0) |
| { |
| // there *is* a previous item in the list |
| Rectangle nxtBounds = lui.getCellBounds(this, curIdx - 1, curIdx - 1); |
| return -nxtBounds.height; |
| } |
| else |
| { |
| // no previous item, no advance possible |
| return 0; |
| } |
| } |
| else |
| { |
| // we are part way through an existing cell, so we are being |
| // asked to scroll to the top of it |
| return curBounds.y - topLeft.y; |
| } |
| } |
| } |
| |
| // FIXME: handle horizontal scrolling (also wrapping?) |
| return 1; |
| } |
| |
| /** |
| * <p>Return the number of pixels the list must scroll in order to move a |
| * "block" of the list into the provided visible rectangle. When the |
| * provided direction is positive, the call describes a "downwards" |
| * scroll, which will be exposing a cell at a <em>greater</em> index in |
| * the list than those elements currently showing. Then the provided |
| * direction is negative, the call describes an "upwards" scroll, which |
| * will be exposing a cell at a <em>lesser</em> index in the list than |
| * those elements currently showing.</p> |
| * |
| * <p>If the provided orientation is <code>HORIZONTAL</code>, the above |
| * comments refer to "rightwards" for positive direction, and "leftwards" |
| * for negative.</p> |
| * |
| * |
| * @param visibleRect The rectangle to scroll an element into |
| * @param orientation One of the numeric consants <code>VERTICAL</code> |
| * or <code>HORIZONTAL</code> |
| * @param direction An integer indicating the scroll direction: positive means |
| * forwards (down, right), negative means backwards (up, left) |
| * |
| * @return The scrollable unit increment, in pixels |
| */ |
| public int getScrollableBlockIncrement(Rectangle visibleRect, |
| int orientation, int direction) |
| { |
| if (orientation == VERTICAL) |
| return visibleRect.height * direction; |
| else |
| return visibleRect.width * direction; |
| } |
| |
| /** |
| * Gets the value of the <code>scrollableTracksViewportWidth</code> property. |
| * |
| * @return <code>true</code> if the viewport is larger (horizontally) |
| * than the list and the list should be expanded to fit the viewport; |
| * <code>false</code> if the viewport is smaller than the list and the |
| * list should scroll (horizontally) within the viewport |
| */ |
| public boolean getScrollableTracksViewportWidth() |
| { |
| Component parent = getParent(); |
| boolean retVal = false; |
| if (parent instanceof JViewport) |
| { |
| JViewport viewport = (JViewport) parent; |
| Dimension pref = getPreferredSize(); |
| if (viewport.getSize().width > pref.width) |
| retVal = true; |
| if ((getLayoutOrientation() == HORIZONTAL_WRAP) |
| && (getVisibleRowCount() <= 0)) |
| retVal = true; |
| } |
| return retVal; |
| } |
| |
| /** |
| * Gets the value of the </code>scrollableTracksViewportWidth</code> property. |
| * |
| * @return <code>true</code> if the viewport is larger (vertically) |
| * than the list and the list should be expanded to fit the viewport; |
| * <code>false</code> if the viewport is smaller than the list and the |
| * list should scroll (vertically) within the viewport |
| */ |
| public boolean getScrollableTracksViewportHeight() |
| { |
| Component parent = getParent(); |
| boolean retVal = false; |
| if (parent instanceof JViewport) |
| { |
| JViewport viewport = (JViewport) parent; |
| Dimension pref = getPreferredSize(); |
| if (viewport.getSize().height > pref.height) |
| retVal = true; |
| if ((getLayoutOrientation() == VERTICAL_WRAP) |
| && (getVisibleRowCount() <= 0)) |
| retVal = true; |
| } |
| return retVal; |
| } |
| |
| /** |
| * Returns the index of the anchor item in the current selection, or |
| * <code>-1</code> if there is no anchor item. |
| * |
| * @return The item index. |
| */ |
| public int getAnchorSelectionIndex() |
| { |
| return selectionModel.getAnchorSelectionIndex(); |
| } |
| |
| /** |
| * Returns the index of the lead item in the current selection, or |
| * <code>-1</code> if there is no lead item. |
| * |
| * @return The item index. |
| */ |
| public int getLeadSelectionIndex() |
| { |
| return selectionModel.getLeadSelectionIndex(); |
| } |
| |
| /** |
| * Returns the lowest item index in the current selection, or <code>-1</code> |
| * if there is no selection. |
| * |
| * @return The index. |
| * |
| * @see #getMaxSelectionIndex() |
| */ |
| public int getMinSelectionIndex() |
| { |
| return selectionModel.getMinSelectionIndex(); |
| } |
| |
| /** |
| * Returns the highest item index in the current selection, or |
| * <code>-1</code> if there is no selection. |
| * |
| * @return The index. |
| * |
| * @see #getMinSelectionIndex() |
| */ |
| public int getMaxSelectionIndex() |
| { |
| return selectionModel.getMaxSelectionIndex(); |
| } |
| |
| /** |
| * Clears the current selection. |
| */ |
| public void clearSelection() |
| { |
| selectionModel.clearSelection(); |
| } |
| |
| /** |
| * Sets the current selection to the items in the specified range (inclusive). |
| * Note that <code>anchor</code> can be less than, equal to, or greater than |
| * <code>lead</code>. |
| * |
| * @param anchor the index of the anchor item. |
| * @param lead the index of the anchor item. |
| */ |
| public void setSelectionInterval(int anchor, int lead) |
| { |
| selectionModel.setSelectionInterval(anchor, lead); |
| } |
| |
| /** |
| * Adds the specified interval to the current selection. Note that |
| * <code>anchor</code> can be less than, equal to, or greater than |
| * <code>lead</code>. |
| * |
| * @param anchor the index of the anchor item. |
| * @param lead the index of the lead item. |
| */ |
| public void addSelectionInterval(int anchor, int lead) |
| { |
| selectionModel.addSelectionInterval(anchor, lead); |
| } |
| |
| /** |
| * Removes the specified interval from the current selection. Note that |
| * <code>index0</code> can be less than, equal to, or greater than |
| * <code>index1</code>. |
| * |
| * @param index0 an index for one end of the range. |
| * @param index1 an index for the other end of the range. |
| */ |
| public void removeSelectionInterval(int index0, int index1) |
| { |
| selectionModel.removeSelectionInterval(index0, index1); |
| } |
| |
| /** |
| * Returns the <code>valueIsAdjusting</code> flag from the list's selection |
| * model. |
| * |
| * @return the value |
| */ |
| public boolean getValueIsAdjusting() |
| { |
| return selectionModel.getValueIsAdjusting(); |
| } |
| |
| /** |
| * Sets the <code>valueIsAdjusting</code> flag in the list's selection |
| * model. |
| * |
| * @param isAdjusting the new value |
| */ |
| public void setValueIsAdjusting(boolean isAdjusting) |
| { |
| selectionModel.setValueIsAdjusting(isAdjusting); |
| } |
| |
| /** |
| * Return the value of the <code>dragEnabled</code> property. |
| * |
| * @return the value |
| * |
| * @since 1.4 |
| */ |
| public boolean getDragEnabled() |
| { |
| return dragEnabled; |
| } |
| |
| /** |
| * Set the <code>dragEnabled</code> property. |
| * |
| * @param enabled new value |
| * |
| * @since 1.4 |
| */ |
| public void setDragEnabled(boolean enabled) |
| { |
| dragEnabled = enabled; |
| } |
| |
| /** |
| * Returns the layout orientation, which will be one of {@link #VERTICAL}, |
| * {@link #VERTICAL_WRAP} and {@link #HORIZONTAL_WRAP}. The default value |
| * is {@link #VERTICAL}. |
| * |
| * @return the orientation. |
| * |
| * @see #setLayoutOrientation(int) |
| * @since 1.4 |
| */ |
| public int getLayoutOrientation() |
| { |
| return layoutOrientation; |
| } |
| |
| /** |
| * Sets the layout orientation (this is a bound property with the name |
| * 'layoutOrientation'). Valid orientations are {@link #VERTICAL}, |
| * {@link #VERTICAL_WRAP} and {@link #HORIZONTAL_WRAP}. |
| * |
| * @param orientation the orientation. |
| * |
| * @throws IllegalArgumentException if <code>orientation</code> is not one |
| * of the specified values. |
| * @since 1.4 |
| * @see #getLayoutOrientation() |
| */ |
| public void setLayoutOrientation(int orientation) |
| { |
| if (orientation < JList.VERTICAL || orientation > JList.HORIZONTAL_WRAP) |
| throw new IllegalArgumentException(); |
| if (layoutOrientation == orientation) |
| return; |
| |
| int old = layoutOrientation; |
| layoutOrientation = orientation; |
| firePropertyChange("layoutOrientation", old, orientation); |
| } |
| |
| /** |
| * Returns the bounds of the rectangle that encloses both list cells |
| * with index0 and index1. |
| * |
| * @param index0 the index of the first cell |
| * @param index1 the index of the second cell |
| * |
| * @return the bounds of the rectangle that encloses both list cells |
| * with index0 and index1, <code>null</code> if one of the indices is |
| * not valid |
| */ |
| public Rectangle getCellBounds(int index0, int index1) |
| { |
| ListUI ui = getUI(); |
| Rectangle bounds = null; |
| if (ui != null) |
| { |
| bounds = ui.getCellBounds(this, index0, index1); |
| } |
| // When the UI is null, this method also returns null in the RI. |
| return bounds; |
| } |
| |
| /** |
| * Returns the index of the next list element (beginning at |
| * <code>startIndex</code> and moving in the specified direction through the |
| * list, looping around if necessary) that starts with <code>prefix</code> |
| * (ignoring case). |
| * |
| * @param prefix the prefix to search for in the cell values |
| * @param startIndex the index where to start searching from |
| * @param direction the search direction, either {@link Position.Bias#Forward} |
| * or {@link Position.Bias#Backward} (<code>null</code> is interpreted |
| * as {@link Position.Bias#Backward}. |
| * |
| * @return the index of the found element or -1 if no such element has |
| * been found |
| * |
| * @throws IllegalArgumentException if prefix is <code>null</code> or |
| * startIndex is not valid |
| * |
| * @since 1.4 |
| */ |
| public int getNextMatch(String prefix, int startIndex, |
| Position.Bias direction) |
| { |
| if (prefix == null) |
| throw new IllegalArgumentException("The argument 'prefix' must not be" |
| + " null."); |
| if (startIndex < 0) |
| throw new IllegalArgumentException("The argument 'startIndex' must not" |
| + " be less than zero."); |
| |
| int size = model.getSize(); |
| if (startIndex >= model.getSize()) |
| throw new IllegalArgumentException("The argument 'startIndex' must not" |
| + " be greater than the number of" |
| + " elements in the ListModel."); |
| |
| int result = -1; |
| int current = startIndex; |
| int delta = -1; |
| int itemCount = model.getSize(); |
| boolean finished = false; |
| prefix = prefix.toUpperCase(); |
| |
| if (direction == Position.Bias.Forward) |
| delta = 1; |
| while (!finished) |
| { |
| String itemStr = model.getElementAt(current).toString().toUpperCase(); |
| if (itemStr.startsWith(prefix)) |
| return current; |
| current = (current + delta); |
| if (current == -1) |
| current += itemCount; |
| else |
| current = current % itemCount; |
| finished = current == startIndex; |
| } |
| return result; |
| } |
| |
| /** |
| * Returns a string describing the attributes for the <code>JList</code> |
| * component, for use in debugging. The return value is guaranteed to be |
| * non-<code>null</code>, but the format of the string may vary between |
| * implementations. |
| * |
| * @return A string describing the attributes of the <code>JList</code>. |
| */ |
| protected String paramString() |
| { |
| StringBuffer sb = new StringBuffer(super.paramString()); |
| sb.append(",fixedCellHeight=").append(getFixedCellHeight()); |
| sb.append(",fixedCellWidth=").append(getFixedCellWidth()); |
| sb.append(",selectionBackground="); |
| if (getSelectionBackground() != null) |
| sb.append(getSelectionBackground()); |
| sb.append(",selectionForeground="); |
| if (getSelectionForeground() != null) |
| sb.append(getSelectionForeground()); |
| sb.append(",visibleRowCount=").append(getVisibleRowCount()); |
| sb.append(",layoutOrientation=").append(getLayoutOrientation()); |
| return sb.toString(); |
| } |
| } |