summaryrefslogtreecommitdiff
path: root/libjava/classpath/javax/swing/JList.java
diff options
context:
space:
mode:
Diffstat (limited to 'libjava/classpath/javax/swing/JList.java')
-rw-r--r--libjava/classpath/javax/swing/JList.java1416
1 files changed, 1416 insertions, 0 deletions
diff --git a/libjava/classpath/javax/swing/JList.java b/libjava/classpath/javax/swing/JList.java
new file mode 100644
index 00000000000..fb8d18b8565
--- /dev/null
+++ b/libjava/classpath/javax/swing/JList.java
@@ -0,0 +1,1416 @@
+/* JList.java --
+ Copyright (C) 2002, 2003, 2004, 2005 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.Dimension;
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.util.Vector;
+
+import javax.accessibility.Accessible;
+import javax.accessibility.AccessibleContext;
+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
+{
+ 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 that the list's selection is currently
+ * "adjusting" -- perhaps due to a user actively dragging the mouse over
+ * multiple list elements -- and is therefore likely to change again in
+ * the near future. A {@link ListSelectionListener} might choose to delay
+ * updating its view of the list's selection until this property is
+ * false, meaning that the adjustment has completed.
+ */
+ boolean valueIsAdjusting;
+
+ /**
+ * This property indicates a <em>preference</em> for the number of rows
+ * displayed in the list, and will scale the
+ * {@link #preferredScrollableViewportSize} property accordingly. The actual
+ * number of displayed rows, when the list is placed in a real {@link
+ * Viewport} or other component, may be greater or less than this number.
+ */
+ int visibleRowCount;
+
+ /**
+ * Fire a {@link ListSelectionEvent} to all the registered ListSelectionListeners.
+ */
+ 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 JList object.
+ */
+ public JList()
+ {
+ init();
+ }
+
+ /**
+ * Creates a new JList object.
+ *
+ * @param listData Initial data to populate the list with
+ */
+ public JList(Object[] listData)
+ {
+ init();
+ setListData(listData);
+ }
+
+ /**
+ * Creates a new JList object.
+ *
+ * @param listData Initial data to populate the list with
+ */
+ public JList(Vector listData)
+ {
+ init();
+ setListData(listData);
+ }
+
+ /**
+ * Creates a new JList object.
+ *
+ * @param listData Initial data to populate the list with
+ */
+ public JList(ListModel listData)
+ {
+ init();
+ setModel(listData);
+ }
+
+ void init()
+ {
+ dragEnabled = false;
+ fixedCellHeight = -1;
+ fixedCellWidth = -1;
+ layoutOrientation = VERTICAL;
+ opaque = true;
+ valueIsAdjusting = false;
+ visibleRowCount = 8;
+
+ cellRenderer = new DefaultListCellRenderer();
+ listListener = new ListListener();
+
+ setModel(new DefaultListModel());
+ setSelectionModel(createSelectionModel());
+
+ 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}.
+ *
+ * @see #getFixedCellHeight
+ * @see #getPrototypeCellValue
+ */
+ public void setFixedCellHeight(int h)
+ {
+ if (fixedCellHeight == h)
+ return;
+
+ int old = fixedCellHeight;
+ fixedCellHeight = h;
+ firePropertyChange("fixedCellWidth", 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}.
+ *
+ * @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.
+ *
+ * @return the current value of the property.
+ */
+
+ public int getVisibleRowCount()
+ {
+ return visibleRowCount;
+ }
+
+ /**
+ * Sets the value of the {@link #visibleRowCount} property.
+ *
+ * @param visibleRowCount The new property value
+ */
+ public void setVisibleRowCount(int vc)
+ {
+ visibleRowCount = 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);
+ }
+
+ 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.
+ *
+ * @see #setSelectionMode
+ * @see #selectionModel
+ */
+ public void setSelectedIndex(int a)
+ {
+ 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>.
+ *
+ * @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
+ * {@link #visibleRect} property, depending on the {@link
+ * #componentOrientation} 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(), 0);
+ return getUI().locationToIndex(this, r.getLocation());
+ }
+
+
+ /**
+ * Returns index of the cell to which specified location is closest to
+ * @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){
+ //FIXME: Need to implement.
+ return null;
+ }
+
+ /**
+ * Returns the list index of the lower right or lower left corner of the
+ * {@link #visibleRect} property, depending on the {@link
+ * #componentOrientation} property.
+ *
+ * @return The index of the first 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());
+ if (or == ComponentOrientation.LEFT_TO_RIGHT)
+ r.translate((int) r.getWidth(), 0);
+ 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 #selection} 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 getSelectedValue
+ */
+ public Object[] getSelectedValues()
+ {
+ int [] idx = getSelectedIndices();
+ Object [] v = new Object[idx.length];
+ for (int i = 0; i < idx.length; ++i)
+ v[i] = getModel().getElementAt(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)
+ {
+ scrollRectToVisible(getUI().getCellBounds(this, i, i));
+ }
+
+ /**
+ * 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(final Object[] listData)
+ {
+ setModel(new AbstractListModel()
+ {
+ public int getSize()
+ {
+ return listData.length;
+ }
+
+ public Object getElementAt(int i)
+ {
+ return listData[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(final Vector listData)
+ {
+ setModel(new AbstractListModel()
+ {
+ public int getSize()
+ {
+ return listData.size();
+ }
+
+ public Object getElementAt(int i)
+ {
+ return listData.elementAt(i);
+ }
+ });
+ }
+
+ /**
+ * 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 #celLRenderer} 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 property value
+ */
+ public void setModel(ListModel model)
+ {
+ 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();
+ }
+
+
+ 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 null;
+ }
+
+ /**
+ * 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 Viewport} to define scrollable objects.
+ *
+ * @return The preferred size
+ */
+ public Dimension getPreferredScrollableViewportSize()
+ {
+
+ Dimension retVal = getPreferredSize();
+ if (getLayoutOrientation() == VERTICAL)
+ {
+ if (fixedCellHeight != -1)
+ {
+ if (fixedCellWidth != -1)
+ {
+ int size = getModel().getSize();
+ retVal = new Dimension(fixedCellWidth, size * fixedCellHeight);
+ } // TODO: add else clause (preferredSize is ok for now)
+ } // TODO: add else clause (preferredSize is ok for now)
+ }
+ return retVal;
+ }
+
+ /**
+ * <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 {@link #scrollableTracksViewportWidth} 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 {@link #scrollableTracksViewportWidth} 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;
+ }
+
+ public int getAnchorSelectionIndex()
+ {
+ return selectionModel.getAnchorSelectionIndex();
+ }
+
+ public int getLeadSelectionIndex()
+ {
+ return selectionModel.getLeadSelectionIndex();
+ }
+
+ public int getMinSelectionIndex()
+ {
+ return selectionModel.getMaxSelectionIndex();
+ }
+
+ public int getMaxSelectionIndex()
+ {
+ return selectionModel.getMaxSelectionIndex();
+ }
+
+ public void clearSelection()
+ {
+ selectionModel.clearSelection();
+ }
+
+ public void setSelectionInterval(int anchor, int lead)
+ {
+ selectionModel.setSelectionInterval(anchor, lead);
+ }
+
+ public void addSelectionInterval(int anchor, int lead)
+ {
+ selectionModel.addSelectionInterval(anchor, lead);
+ }
+
+ public void removeSelectionInterval(int index0, int index1)
+ {
+ selectionModel.removeSelectionInterval(index0, index1);
+ }
+
+ /**
+ * Returns the value of the <code>valueIsAdjusting</code> property.
+ *
+ * @return the value
+ */
+ public boolean getValueIsAdjusting()
+ {
+ return valueIsAdjusting;
+ }
+
+ /**
+ * Sets the <code>valueIsAdjusting</code> property.
+ *
+ * @param isAdjusting the new value
+ */
+ public void setValueIsAdjusting(boolean isAdjusting)
+ {
+ valueIsAdjusting = 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.
+ *
+ * @return the orientation, one of <code>JList.VERTICAL</code>,
+ * <code>JList.VERTICAL_WRAP</code> and <code>JList.HORIZONTAL_WRAP</code>
+ *
+ * @since 1.4
+ */
+ public int getLayoutOrientation()
+ {
+ return layoutOrientation;
+ }
+
+ /**
+ * Sets the layout orientation.
+ *
+ * @param orientation the orientation to set, one of <code>JList.VERTICAL</code>,
+ * <code>JList.VERTICAL_WRAP</code> and <code>JList.HORIZONTAL_WRAP</code>
+ *
+ * @since 1.4
+ */
+ public void setLayoutOrientation(int orientation)
+ {
+ 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)
+ {
+ return ((ListUI) ui).getCellBounds(this, index0, index1);
+ }
+
+ /**
+ * Returns the next list element (beginning from <code>startIndex</code>
+ * that starts with <code>prefix</code>. Searching is done in the direction
+ * specified by <code>bias</code>.
+ *
+ * @param prefix the prefix to search for in the cell values
+ * @param startIndex the index where to start searching from
+ * @param bias the search direction, either {@link Position.Bias.Forward}
+ * or {@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 bias)
+ {
+ 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 index = -1;
+ if (bias == Position.Bias.Forward)
+ {
+ for (int i = startIndex; i < size; i++)
+ {
+ String item = model.getElementAt(i).toString();
+ if (item.startsWith(prefix))
+ {
+ index = i;
+ break;
+ }
+ }
+ }
+ else
+ {
+ for (int i = startIndex; i >= 0; i--)
+ {
+ String item = model.getElementAt(i).toString();
+ if (item.startsWith(prefix))
+ {
+ index = i;
+ break;
+ }
+ }
+ }
+ return index;
+ }
+}