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 JList. + */ + protected class AccessibleJList extends AccessibleJComponent + implements AccessibleSelection, PropertyChangeListener, + ListSelectionListener, ListDataListener + { + + /** + * Provides accessibility support for list elements in JLists. + */ + 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 AccessibleJListChild. + * + * @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 + * this since AccessibleJListChilds are their + * own accessible contexts. + * + * @return the accessible context of this object, this + */ + public AccessibleContext getAccessibleContext() + { + return this; + } + + /** + * Returns the background color for this list child. This returns the + * background of the JList 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 JList 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 JList since it is not possible to + * set fonts for list children individually. + * + * @return the font of the JList + */ + 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 JList. + * + * @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 true if the parent JList is enabled, + * false otherwise. The list children cannot have an enabled + * flag set individually. + * + * @return true if the parent JList is enabled, + * false 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 true if this list child is visible, + * false otherwise. The value of this property depends + * on address@hidden JList#getFirstVisibleIndex()} and + * address@hidden JList#getLastVisibleIndex()}. + * + * @return true if this list child is visible, + * false 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 true if this list child is currently showing on + * screen and false otherwise. The list child is showing if + * it is visible and if it's parent JList is currently showing. + * + * @return true if this list child is currently showing on + * screen and false otherwise + */ + public boolean isShowing() + { + return isVisible() && parent.isShowing(); + } + + /** + * Returns true if this list child covers the screen location + * point (relative to the JList coordinate + * system, false otherwise. + * + * @return true if this list child covers the screen location + * point , false 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 null because list children do not have children + * themselves + * + * @return null + */ + public Accessible getAccessibleAt(Point point) + { + return null; + } + + /** + * Returns true 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 + * address@hidden AccessibleRole#LABEL}. + * + * @return address@hidden 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 0 since list children don't have children + * themselves. + * + * @return 0 + */ + public int getAccessibleChildrenCount() + { + return 0; + } + + /** + * Returns null since list children don't have children + * themselves. + * + * @return null + */ + 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 true if the n-th child is selected, + * false otherwise. + * + * @param n the index of the child of which the selected state is queried + * + * @return true if the n-th child is selected, + * false 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 + * address@hidden AccessibleContext#ACCESSIBLE_VISIBLE_DATA_PROPERTY} and the second + * with address@hidden 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 + * JList. This method fires a property change event with + * address@hidden 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 + * JList. This method fires a property change event with + * address@hidden 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 + * JList. This method fires a property change event with + * address@hidden 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 JList'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 JList. + * + * @return the state set of the JList + */ + 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 JList, + * address@hidden AccessibleRole#LIST}. + * + * @return the accessible role for JList + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.LIST; + } + + /** + * Returns the accessible child at the visual location p + * (relative to the upper left corner of the JList). If there + * is no child at that location, this returns null. + * + * @param p the screen location for which to return the accessible child + * + * @return the accessible child at the specified location, or + * null 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 JList. + * + * @return the number of accessible children in the JList + */ + public int getAccessibleChildrenCount() + { + return getModel().getSize(); + } + + /** + * Returns the n-th accessible child of this JList. This will + * be an instance of address@hidden AccessibleJListChild}. If there is no child + * at that index, null is returned. + * + * @param n the index of the child to return + * + * @return the n-th accessible child of this JList + */ + public Accessible getAccessibleChild(int n) + { + if (getModel().getSize() <= n) + return null; + return new AccessibleJListChild(JList.this, n); + } + } + private static final long serialVersionUID = 4406629526391098046L; /**