Logo Search packages:      
Sourcecode: kaffe version File versions  Download package

JList.java

/* 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.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)
 */

00121 public class JList extends JComponent implements Accessible, Scrollable
{

  /**
   * Provides accessibility support for <code>JList</code>.
   */
00127   protected class AccessibleJList extends AccessibleJComponent
    implements AccessibleSelection, PropertyChangeListener,
               ListSelectionListener, ListDataListener
  {

    /**
     * Provides accessibility support for list elements in <code>JList</code>s.
     */
00135     protected class AccessibleJListChild extends AccessibleContext
      implements Accessible, AccessibleComponent
    {

      /**
       * The parent list.
       */
00142       JList parent;

      /**
       * The index in the list for that child.
       */
00147       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.
00155       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
       */
00163       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>
       */
00176       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
       */
00188       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.
       */
00199       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
       */
00212       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.
       */
00223       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
       */
00234       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.
       */
00245       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>
       */
00259       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
       */
00270       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
       */
00284       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
       */
00297       public boolean isEnabled()
      {
        return parent.isEnabled();
      }

      /**
       * Does nothing since the enabled flag cannot be set for list children
       * individually.
       *
       * @param b not used here
       */
00308       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
       */
00323       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
       */
00335       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
       */
00349       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
       */
00362       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
       */
00372       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)
       */
00386       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
       */
00397       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)
       */
00410       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
       */
00421       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
       */
00432       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
       */
00444       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>
       */
00456       public Accessible getAccessibleAt(Point point)
      {
        return null;
      }

      /**
       * Returns <code>true</code> since list children are focus traversable.
       *
       * @return true
       */
00466       public boolean isFocusTraversable()
      {
        // TODO: Is this 100% ok?
        return true;
      }

      /**
       * Requests focus on the parent list. List children cannot request focus
       * individually.
       */
00476       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
       */
00488       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
       */
00500       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}
       */
00512       public AccessibleRole getAccessibleRole()
      {
        return AccessibleRole.LABEL;
      }

      /**
       * Returns the accessible state set of this list item.
       *
       * @return the accessible state set of this list item
       */
00522       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
       */
00553       public int getAccessibleIndexInParent()
      {
        return listIndex;
      }

      /**
       * Returns <code>0</code> since list children don't have children
       * themselves.
       *
       * @return <code>0</code>
       */
00564       public int getAccessibleChildrenCount()
      {
        return 0;
      }

      /**
       * Returns <code>null</code> since list children don't have children
       * themselves.
       *
       * @return <code>null</code>
       */
00575       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
       */
00586       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
       */
00598       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
       */
00610       public void removePropertyChangeListener(PropertyChangeListener l)
      {
        // Do nothing here.
      }
      
      // TODO: Implement the remaining methods of this class.
    }
    
    /**
     * Create a new AccessibleJList.
     */
00621     public AccessibleJList()
    {
      // Nothing to do here.
    }

    /**
     * Returns the number of selected accessible children.
     *
     * @return the number of selected accessible children
     */
00631     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
     */
00643     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
     */
00657     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
     */
00669     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
     */
00679     public void removeAccessibleSelection(int i)
    {
      removeSelectionInterval(i, i);
    }

    /**
     * Remove all selection items from the selection.
     */
00687     public void clearAccessibleSelection()
    {
      clearSelection();
    }

    /**
     * Selects all items if multiple selections are supported.
     * Otherwise do nothing.
     */
00696     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
     */
00709     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
     */
00724     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
     */
00737     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
     */
00750     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
     */
00765     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>
     */
00789     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>
     */
00805     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
     */
00820     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>
     */
00831     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>
     */
00845     public Accessible getAccessibleChild(int n)
    {
      if (getModel().getSize() <= n)
        return null;
      return new AccessibleJListChild(JList.this, n);
    }
  }

00853   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. 
   */
00859   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. 
   */
00866   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. 
   */
00873   public static final int HORIZONTAL_WRAP = 2;

  /**
   * This property indicates whether "drag and drop" functions are enabled
   * on the list.
   */
00879   boolean dragEnabled;

  /** This property provides a strategy for rendering cells in the list. */
00882   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.
   */
00890   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.
   */
00898   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}. 
   */
00905   int layoutOrientation;
  
  /** This property holds the data elements displayed by the list. */
00908   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>
   */
00922   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.
   */
00930   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.
   */
00938   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.
   */
00945   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.
   */
00956   boolean valueIsAdjusting;

  /** 
   * 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.
   */
00965   int visibleRowCount;

  /**
   * Fire a {@link ListSelectionEvent} to all the registered ListSelectionListeners.
   */
00970   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.
   */
00988   private class ListListener 
    implements ListSelectionListener, ListDataListener
  {
    // ListDataListener events
00992     public void contentsChanged(ListDataEvent event)
    {
      JList.this.revalidate();
      JList.this.repaint();
    }
00997     public void intervalAdded(ListDataEvent event)
    {
      JList.this.revalidate();
      JList.this.repaint();
    }
01002     public void intervalRemoved(ListDataEvent event)
    {
      JList.this.revalidate();
      JList.this.repaint();
    }
    // ListSelectionListener events
01008     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.
   */
01021   ListListener listListener;


  /**
   * Creates a new JList object.
   */
01027   public JList()
  {
    init();
  }

  /**
   * Creates a new JList object.
   *
   * @param listData Initial data to populate the list with
   */
01037   public JList(Object[] listData)
  {
    init();
    setListData(listData);
  }

  /**
   * Creates a new JList object.
   *
   * @param listData Initial data to populate the list with
   */
01048   public JList(Vector listData)
  {
    init();
    setListData(listData);
  }

  /**
   * Creates a new JList object.
   *
   * @param listData Initial data to populate the list with
   */
01059   public JList(ListModel listData)
  {
    init();
    setModel(listData);
  }

  void init()
  {
    dragEnabled = false;
    fixedCellHeight = -1;
    fixedCellWidth = -1;
    layoutOrientation = VERTICAL;
    opaque = true;
    valueIsAdjusting = false;
    visibleRowCount = 7;

    cellRenderer = new DefaultListCellRenderer();
    listListener = new ListListener();

    setModel(new DefaultListModel());
    setSelectionModel(createSelectionModel());
    setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
    setLayout(null);
    
    updateUI();
  }

  /**
   * Creates the default <code>ListSelectionModel</code>.
   *
   * @return the <code>ListSelectionModel</code>
   */
01091   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
   */
01108   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
   */
01123   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
   */
01145   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
   */
01160   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.
   */

01176   public int getVisibleRowCount()
  {
    return visibleRowCount;
  }

  /**
   * Sets the value of the {@link #visibleRowCount} property. 
   *
   * @param vc The new property value
   */
01186   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
   */
01202   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
   */
01214   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
   */
01227   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
   */
01247   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 &lt; 0 the selection is cleared.
   *
   * @see #setSelectionMode
   * @see #selectionModel
   */
01265   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>.
   *
   * @see #setSelectionMode
   * @see #selectionModel
   */
01280   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
   */
01295   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}
   */
01306   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.
   */
01319   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.
   */
01339    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.
   */
01350    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.
   */
01363   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
   */
01383   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
   */
01410   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
   */
01428   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
   */
01444   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
   */
01458   public Color getSelectionBackground()
  {
    return selectionBackground;
  }

  /**
   * Sets the value of the {@link #selectionBackground} property.
   *
   * @param c The new value of the property
   */
01468   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
   */
01484   public Color getSelectionForeground()
  {
    return selectionForeground;
  }
  
  /**
   * Sets the value of the {@link #selectionForeground} property.
   *
   * @param c The new value of the property
   */
01494   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
   */

01515   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
   */
01537   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
   */
01550   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
   */
01574   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
   */
01595   public ListCellRenderer getCellRenderer()
  {
    return cellRenderer;
  }

  /**
   * Sets the value of the {@link #getCellRenderer} property.
   *
   * @param renderer The new property value
   */
01605   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
   */
01622   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>.
   */
01637   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();
  }


  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
   */
01671   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
   */
01695   public ListUI getUI()
  {
    return (ListUI) ui;
  }

  /**
   * Sets the value of the UI property.
   *
   * @param ui The new property value
   */
01705   public void setUI(ListUI ui)
  {
    super.setUI(ui);
  }

  /**
   * Calls {@link #setUI} with the {@link ListUI} subclass
   * returned from calling {@link UIManager#getUI}.
   */
01714   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
   */
01726   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
   */
01743   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
   */
01763   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);
  }

01778   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
   */
01791   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
   */
01860   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
   */
01954   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
   */
01971   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
   */
01996   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
   */
02058   public boolean getValueIsAdjusting()
  {
    return valueIsAdjusting;
  }

  /**
   * Sets the <code>valueIsAdjusting</code> property.
   *
   * @param isAdjusting the new value
   */
02068   public void setValueIsAdjusting(boolean isAdjusting)
  {
    valueIsAdjusting = isAdjusting;
  }

  /**
   * Return the value of the <code>dragEnabled</code> property.
   *
   * @return the value
   * 
   * @since 1.4
   */
02080   public boolean getDragEnabled()
  {
    return dragEnabled;
  }

  /**
   * Set the <code>dragEnabled</code> property.
   *
   * @param enabled new value
   * 
   * @since 1.4
   */
02092   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
   */
02105   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
   */
02118   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
   */
02139   public Rectangle getCellBounds(int index0, int index1)
  {
    return getUI().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
   */
02162   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;
  }
}

Generated by  Doxygen 1.6.0   Back to index