Hi,

I implemented most of the AccessibleJList inner class of JList.

2005-09-16  Roman Kennke  <[EMAIL PROTECTED]>

        * javax/swing/JList.java
        (AccessibleJList): New inner class.

/Roman
Index: javax/swing/JList.java
===================================================================
RCS file: /cvsroot/classpath/classpath/javax/swing/JList.java,v
retrieving revision 1.31
diff -u -r1.31 JList.java
--- javax/swing/JList.java	2 Sep 2005 16:06:38 -0000	1.31
+++ javax/swing/JList.java	16 Sep 2005 09:32:47 -0000
@@ -41,13 +41,25 @@
 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;
@@ -108,6 +120,728 @@
 
 public class JList extends JComponent implements Accessible, Scrollable
 {
+
+  /**
+   * Provides accessibility support for <code>JList</code>.
+   */
+  protected class AccessibleJList extends AccessibleJComponent
+    implements AccessibleSelection, PropertyChangeListener,
+               ListSelectionListener, ListDataListener
+  {
+
+    /**
+     * Provides accessibility support for list elements in <code>JList</code>s.
+     */
+    protected class AccessibleJListChild extends AccessibleContext
+      implements Accessible, AccessibleComponent
+    {
+
+      /**
+       * The parent list.
+       */
+      JList parent;
+
+      /**
+       * The index in the list for that child.
+       */
+      int listIndex;
+
+      /**
+       * The cursor for this list child.
+       */
+      // TODO: Testcases show that this class somehow stores state about the
+      // cursor. I cannot make up though how that could affect
+      // the actual list.
+      Cursor cursor = Cursor.getDefaultCursor();
+
+      /**
+       * Creates a new instance of <code>AccessibleJListChild</code>.
+       *
+       * @param list the list of which this is an accessible child
+       * @param index the list index for this child
+       */
+      public AccessibleJListChild(JList list, int index)
+      {
+        parent = list;
+        listIndex = index;
+      }
+
+      /**
+       * Returns the accessible context of this object. Returns
+       * <code>this</code> since <code>AccessibleJListChild</code>s are their
+       * own accessible contexts.
+       *
+       * @return the accessible context of this object, <code>this</code>
+       */
+      public AccessibleContext getAccessibleContext()
+      {
+        return this;
+      }
+
+      /**
+       * Returns the background color for this list child. This returns the
+       * background of the <code>JList</code> itself since the background
+       * cannot be set on list children individually
+       *
+       * @return the background color for this list child
+       */
+      public Color getBackground()
+      {
+        return parent.getBackground();
+      }
+
+      /**
+       * Calling this method has no effect, since the background color cannot be
+       * set on list children individually.
+       *
+       * @param color not used here.
+       */
+      public void setBackground(Color color)
+      {
+        // Calling this method has no effect, since the background color cannot
+        // be set on list children individually.
+      }
+
+      /**
+       * Returns the foreground color for this list child. This returns the
+       * background of the <code>JList</code> itself since the foreground
+       * cannot be set on list children individually.
+       *
+       * @return the background color for this list child
+       */
+      public Color getForeground()
+      {
+        return parent.getForeground();
+      }
+
+      /**
+       * Calling this method has no effect, since the foreground color cannot be
+       * set on list children individually.
+       *
+       * @param color not used here.
+       */
+      public void setForeground(Color color)
+      {
+        // Calling this method has no effect, since the foreground color cannot
+        // be set on list children individually.
+      }
+
+      /**
+       * Returns the cursor for this list child.
+       *
+       * @return the cursor for this list child
+       */
+      public Cursor getCursor()
+      {
+        // TODO: Testcases show that this method returns the cursor that has
+        // been set by setCursor. I cannot make up though how that could affect
+        // the actual list.
+        return cursor;
+      }
+
+      /**
+       * Sets the cursor for this list child.
+       */
+      public void setCursor(Cursor cursor)
+      {
+        this.cursor = cursor;
+        // TODO: Testcases show that this method returns the cursor that has
+        // been set by setCursor. I cannot make up though how that could affect
+        // the actual list.
+      }
+
+      /**
+       * Returns the font of the <code>JList</code> since it is not possible to
+       * set fonts for list children individually.
+       *
+       * @return the font of the <code>JList</code>
+       */
+      public Font getFont()
+      {
+        return parent.getFont();
+      }
+
+      /**
+       * Does nothing since it is not possible to set the font on list children
+       * individually.
+       *
+       * @param font not used here
+       */
+      public void setFont(Font font)
+      {
+        // Does nothing since it is not possible to set the font on list
+        // children individually.
+      }
+
+      /**
+       * Returns the font metrics for the specified font. This method forwards
+       * to the parent <code>JList</code>. 
+       *
+       * @param font the font for which the font metrics is queried
+       *
+       * @return the font metrics for the specified font
+       */
+      public FontMetrics getFontMetrics(Font font)
+      {
+        return parent.getFontMetrics(font);
+      }
+
+      /**
+       * Returns <code>true</code> if the parent <code>JList</code> is enabled,
+       * <code>false</code> otherwise. The list children cannot have an enabled
+       * flag set individually.
+       *
+       * @return <code>true</code> if the parent <code>JList</code> is enabled,
+       *         <code>false</code> otherwise
+       */
+      public boolean isEnabled()
+      {
+        return parent.isEnabled();
+      }
+
+      /**
+       * Does nothing since the enabled flag cannot be set for list children
+       * individually.
+       *
+       * @param b not used here
+       */
+      public void setEnabled(boolean b)
+      {
+        // Does nothing since the enabled flag cannot be set for list children
+        // individually.
+      }
+
+      /**
+       * Returns <code>true</code> if this list child is visible,
+       * <code>false</code> otherwise. The value of this property depends
+       * on [EMAIL PROTECTED] JList#getFirstVisibleIndex()} and
+       * [EMAIL PROTECTED] JList#getLastVisibleIndex()}.
+       *
+       * @return <code>true</code> if this list child is visible,
+       *         <code>false</code> otherwise
+       */
+      public boolean isVisible()
+      {
+        return listIndex >= parent.getFirstVisibleIndex()
+               && listIndex <= parent.getLastVisibleIndex();
+      }
+
+      /**
+       * The value of the visible property cannot be modified, so this method
+       * does nothing.
+       *
+       * @param b not used here
+       */
+      public void setVisible(boolean b)
+      {
+        // The value of the visible property cannot be modified, so this method
+        // does nothing.
+      }
+
+      /**
+       * Returns <code>true</code> if this list child is currently showing on
+       * screen and <code>false</code> otherwise. The list child is showing if
+       * it is visible and if it's parent JList is currently showing.
+       *
+       * @return <code>true</code> if this list child is currently showing on
+       *         screen and <code>false</code> otherwise
+       */
+      public boolean isShowing()
+      {
+        return isVisible() && parent.isShowing();
+      }
+
+      /**
+       * Returns <code>true</code> if this list child covers the screen location
+       * <code>point</code> (relative to the <code>JList</code> coordinate
+       * system, <code>false</code> otherwise.
+       *
+       * @return <code>true</code> if this list child covers the screen location
+       *         <code>point</code> , <code>false</code> otherwise
+       */
+      public boolean contains(Point point)
+      {
+        return getBounds().contains(point);
+      }
+
+      /**
+       * Returns the absolute screen location of this list child.
+       *
+       * @return the absolute screen location of this list child
+       */
+      public Point getLocationOnScreen()
+      {
+        Point loc = getLocation();
+        SwingUtilities.convertPointToScreen(loc, parent);
+        return loc;
+      }
+
+      /**
+       * Returns the screen location of this list child relative to it's parent.
+       *
+       * @return the location of this list child relative to it's parent
+       *
+       * @see JList#indexToLocation(int)
+       */
+      public Point getLocation()
+      {
+        return parent.indexToLocation(listIndex);
+      }
+
+      /**
+       * Does nothing since the screen location cannot be set on list children
+       * explictitly.
+       *
+       * @param point not used here
+       */
+      public void setLocation(Point point)
+      {
+        // Does nothing since the screen location cannot be set on list children
+        // explictitly.
+      }
+
+      /**
+       * Returns the bounds of this list child.
+       *
+       * @return the bounds of this list child
+       *
+       * @see JList#getCellBounds(int, int)
+       */
+      public Rectangle getBounds()
+      {
+        return parent.getCellBounds(listIndex, listIndex);
+      }
+
+      /**
+       * Does nothing since the bounds cannot be set on list children
+       * individually.
+       *
+       * @param rectangle not used here
+       */
+      public void setBounds(Rectangle rectangle)
+      {
+        // Does nothing since the bounds cannot be set on list children
+        // individually.
+      }
+
+      /**
+       * Returns the size of this list child.
+       *
+       * @return the size of this list child
+       */
+      public Dimension getSize()
+      {
+        Rectangle b = getBounds();
+        return b.getSize();
+      }
+
+      /**
+       * Does nothing since the size cannot be set on list children
+       * individually.
+       *
+       * @param dimension not used here
+       */
+      public void setSize(Dimension dimension)
+      {
+        // Does nothing since the size cannot be set on list children
+        // individually.
+      }
+
+      /**
+       * Returns <code>null</code> because list children do not have children
+       * themselves
+       *
+       * @return <code>null</code>
+       */
+      public Accessible getAccessibleAt(Point point)
+      {
+        return null;
+      }
+
+      /**
+       * Returns <code>true</code> since list children are focus traversable.
+       *
+       * @return true
+       */
+      public boolean isFocusTraversable()
+      {
+        // TODO: Is this 100% ok?
+        return true;
+      }
+
+      /**
+       * Requests focus on the parent list. List children cannot request focus
+       * individually.
+       */
+      public void requestFocus()
+      {
+        // TODO: Is this 100% ok?
+        parent.requestFocus();
+      }
+
+      /**
+       * Adds a focus listener to the parent list. List children do not have
+       * their own focus management.
+       *
+       * @param listener the focus listener to add
+       */
+      public void addFocusListener(FocusListener listener)
+      {
+        // TODO: Is this 100% ok?
+        parent.addFocusListener(listener);
+      }
+
+      /**
+       * Removes a focus listener from the parent list. List children do not
+       * have their own focus management.
+       *
+       * @param listener the focus listener to remove
+       */
+      public void removeFocusListener(FocusListener listener)
+      {
+        // TODO: Is this 100%
+        parent.removeFocusListener(listener);
+      }
+
+      /**
+       * Returns the accessible role of this list item, which is
+       * [EMAIL PROTECTED] AccessibleRole#LABEL}.
+       *
+       * @return [EMAIL PROTECTED] AccessibleRole#LABEL}
+       */
+      public AccessibleRole getAccessibleRole()
+      {
+        return AccessibleRole.LABEL;
+      }
+
+      /**
+       * Returns the accessible state set of this list item.
+       *
+       * @return the accessible state set of this list item
+       */
+      public AccessibleStateSet getAccessibleStateSet()
+      {
+        AccessibleStateSet states = new AccessibleStateSet();
+        if (isVisible())
+          states.add(AccessibleState.VISIBLE);
+        if (isShowing())
+          states.add(AccessibleState.SHOWING);
+        if (isFocusTraversable())
+          states.add(AccessibleState.FOCUSABLE);
+        // TODO: How should the active state be handled? The API docs
+        // suggest that this state is set on the activated list child,
+        // that is the one that is drawn with a box. However, I don't know how
+        // to implement this.
+
+        // TODO: We set the selectable state here because list children are
+        // selectable. Is there a way to disable single children?
+        if (parent.isEnabled())
+          states.add(AccessibleState.SELECTABLE);
+ 
+        if (parent.isSelectedIndex(listIndex))
+          states.add(AccessibleState.SELECTED);
+
+        // TODO: Handle more states here?
+        return states;
+      }
+
+      /**
+       * Returns the index of this list child within it's parent list.
+       *
+       * @return the index of this list child within it's parent list
+       */
+      public int getAccessibleIndexInParent()
+      {
+        return listIndex;
+      }
+
+      /**
+       * Returns <code>0</code> since list children don't have children
+       * themselves.
+       *
+       * @return <code>0</code>
+       */
+      public int getAccessibleChildrenCount()
+      {
+        return 0;
+      }
+
+      /**
+       * Returns <code>null</code> since list children don't have children
+       * themselves.
+       *
+       * @return <code>null</code>
+       */
+      public Accessible getAccessibleChild(int i)
+      {
+        return null;
+      }
+
+      /**
+       * Returns the locale of this component. This call is forwarded to the
+       * parent list since list children don't have a separate locale setting.
+       *
+       * @return the locale of this component
+       */
+      public Locale getLocale()
+      {
+        return parent.getLocale();
+      }
+
+      /**
+       * This method does
+       * nothing, list children are transient accessible objects which means
+       * that they don't fire property change events.
+       *
+       * @param l not used here
+       */
+      public void addPropertyChangeListener(PropertyChangeListener l)
+      {
+        // Do nothing here.
+      }
+
+      /**
+       * This method does
+       * nothing, list children are transient accessible objects which means
+       * that they don't fire property change events.
+       *
+       * @param l not used here
+       */
+      public void removePropertyChangeListener(PropertyChangeListener l)
+      {
+        // Do nothing here.
+      }
+      
+      // TODO: Implement the remaining methods of this class.
+    }
+
+    /**
+     * Returns the number of selected accessible children.
+     *
+     * @return the number of selected accessible children
+     */
+    public int getAccessibleSelectionCount()
+    {
+      return getSelectedIndices().length;
+    }
+
+    /**
+     * Returns the n-th selected accessible child.
+     *
+     * @param n the index of the selected child to return
+     *
+     * @return the n-th selected accessible child
+     */
+    public Accessible getAccessibleSelection(int n)
+    {
+      return new AccessibleJListChild(JList.this, getSelectedIndices()[n]);
+    }
+
+    /**
+     * Returns <code>true</code> if the n-th child is selected,
+     * <code>false</code> otherwise.
+     *
+     * @param n the index of the child of which the selected state is queried
+     *
+     * @return <code>true</code> if the n-th child is selected,
+     *         <code>false</code> otherwise
+     */
+    public boolean isAccessibleChildSelected(int n)
+    {
+      return isSelectedIndex(n);
+    }
+
+    /**
+     * Adds the accessible item with the specified index to the selected items.
+     * If multiple selections are supported, the item is added to the selection,
+     * otherwise the item replaces the current selection.
+     *
+     * @param i the index of the item to add to the selection
+     */
+    public void addAccessibleSelection(int i)
+    {
+      addSelectionInterval(i, i);
+    }
+
+    /**
+     * Removes the accessible item with the specified index to the selection.
+     *
+     * @param i the index of the item to be removed from the selection
+     */
+    public void removeAccessibleSelection(int i)
+    {
+      removeSelectionInterval(i, i);
+    }
+
+    /**
+     * Remove all selection items from the selection.
+     */
+    public void clearAccessibleSelection()
+    {
+      clearSelection();
+    }
+
+    /**
+     * Selects all items if multiple selections are supported.
+     * Otherwise do nothing.
+     */
+    public void selectAllAccessibleSelection()
+    {
+      addSelectionInterval(0, getModel().getSize());
+    }
+
+    /**
+     * Receices notification when the list selection is changed. This method
+     * fires two property change events, the first with
+     * [EMAIL PROTECTED] AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY} and the second
+     * with [EMAIL PROTECTED] AccessibleContext#ACCESSIBLE_SELECTION_PROPERTY}.
+     *
+     * @param event the list selection event
+     */
+    public void valueChanged(ListSelectionEvent event)
+    {
+      firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE,
+                         Boolean.TRUE);
+      firePropertyChange(ACCESSIBLE_SELECTION_PROPERTY, Boolean.FALSE,
+                         Boolean.TRUE);
+    }
+
+    /**
+     * Receives notification when items have changed in the
+     * <code>JList</code>. This method fires a property change event with
+     * [EMAIL PROTECTED] AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}.
+     *
+     * @param event the list data event
+     */
+    public void contentsChanged(ListDataEvent event)
+    {
+      firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE,
+                         Boolean.TRUE);
+    }
+
+    /**
+     * Receives notification when items are inserted into the
+     * <code>JList</code>. This method fires a property change event with
+     * [EMAIL PROTECTED] AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}.
+     *
+     * @param event the list data event
+     */
+    public void intervalAdded(ListDataEvent event)
+    {
+      firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE,
+                         Boolean.TRUE);
+    }
+
+    /**
+     * Receives notification when items are removed from the
+     * <code>JList</code>. This method fires a property change event with
+     * [EMAIL PROTECTED] AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY}.
+     *
+     * @param event the list data event
+     */
+    public void intervalRemoved(ListDataEvent event)
+    {
+      firePropertyChange(ACCESSIBLE_VISIBLE_DATA_PROPERTY, Boolean.FALSE,
+                         Boolean.TRUE);
+    }
+
+
+    /**
+     * Receives notification about changes of the <code>JList</code>'s
+     * properties. This is used to re-register this object as listener to
+     * the data model and selection model when the data model or selection model
+     * changes.
+     *
+     * @param e the property change event
+     */
+    public void propertyChange(PropertyChangeEvent e)
+    {
+      String propertyName = e.getPropertyName();
+      if (propertyName.equals("model"))
+        {
+          ListModel oldModel = (ListModel) e.getOldValue();
+          oldModel.removeListDataListener(this);
+          ListModel newModel = (ListModel) e.getNewValue();
+          newModel.addListDataListener(this);
+        }
+      else if (propertyName.equals("selectionModel"))
+        {
+          ListSelectionModel oldModel = (ListSelectionModel) e.getOldValue();
+          oldModel.removeListSelectionListener(this);
+          ListSelectionModel newModel = (ListSelectionModel) e.getNewValue();
+          oldModel.addListSelectionListener(this);
+        }
+    }
+
+    /**
+     * Return the state set of the <code>JList</code>.
+     *
+     * @return the state set of the <code>JList</code>
+     */
+    public AccessibleStateSet getAccessibleStateSet()
+    {
+      // TODO: Figure out if there is possibly more state that must be
+      // handled here.
+      AccessibleStateSet s = super.getAccessibleStateSet();
+      if (getSelectionMode() != ListSelectionModel.SINGLE_SELECTION)
+        s.add(AccessibleState.MULTISELECTABLE);
+      return s;
+    }
+
+    /**
+     * Returns the accessible role for <code>JList</code>,
+     * [EMAIL PROTECTED] AccessibleRole#LIST}.
+     *
+     * @return the accessible role for <code>JList</code>
+     */
+    public AccessibleRole getAccessibleRole()
+    {
+      return AccessibleRole.LIST;
+    }
+
+    /**
+     * Returns the accessible child at the visual location <code>p</code>
+     * (relative to the upper left corner of the <code>JList</code>). If there
+     * is no child at that location, this returns <code>null</code>.
+     *
+     * @param p the screen location for which to return the accessible child
+     *
+     * @return the accessible child at the specified location, or
+     *         <code>null</code> if there is no child at that location
+     */
+    public Accessible getAccessibleAt(Point p)
+    {
+      int childIndex = locationToIndex(p);
+      return getAccessibleChild(childIndex);
+    }
+
+    /**
+     * Returns the number of accessible children in the <code>JList</code>.
+     *
+     * @return the number of accessible children in the <code>JList</code>
+     */
+    public int getAccessibleChildrenCount()
+    {
+      return getModel().getSize();
+    }
+
+    /**
+     * Returns the n-th accessible child of this <code>JList</code>. This will
+     * be an instance of [EMAIL PROTECTED] AccessibleJListChild}. If there is no child
+     * at that index, <code>null</code> is returned.
+     *
+     * @param n the index of the child to return
+     *
+     * @return the n-th accessible child of this <code>JList</code>
+     */
+    public Accessible getAccessibleChild(int n)
+    {
+      if (getModel().getSize() <= n)
+        return null;
+      return new AccessibleJListChild(JList.this, n);
+    }
+  }
+
   private static final long serialVersionUID = 4406629526391098046L;
 
   /** 
_______________________________________________
Classpath-patches mailing list
[email protected]
http://lists.gnu.org/mailman/listinfo/classpath-patches

Reply via email to