diff options
Diffstat (limited to 'javax/swing/JTable.java')
-rw-r--r-- | javax/swing/JTable.java | 1424 |
1 files changed, 1233 insertions, 191 deletions
diff --git a/javax/swing/JTable.java b/javax/swing/JTable.java index 348534469..4f93da8d2 100644 --- a/javax/swing/JTable.java +++ b/javax/swing/JTable.java @@ -63,6 +63,7 @@ import javax.accessibility.AccessibleContext; import javax.accessibility.AccessibleExtendedTable; import javax.accessibility.AccessibleRole; import javax.accessibility.AccessibleSelection; +import javax.accessibility.AccessibleState; import javax.accessibility.AccessibleStateSet; import javax.accessibility.AccessibleTable; import javax.accessibility.AccessibleTableModelChange; @@ -165,6 +166,8 @@ public class JTable public AccessibleRole getAccessibleRole() { // TODO: What is the role of the table cell? + // Seems like the RI returns UNKNOWN here for 'normal' cells, might + // be different for special renderers though (not tested yet). return AccessibleRole.UNKNOWN; } @@ -175,8 +178,27 @@ public class JTable */ public AccessibleStateSet getAccessibleStateSet() { - // TODO: What state shoiuld be returned here? - return new AccessibleStateSet(); + AccessibleStateSet state = new AccessibleStateSet(); + + // Figure out the SHOWING state. + Rectangle visibleRect = getVisibleRect(); + Rectangle cellRect = getCellRect(row, column, false); + if (visibleRect.intersects(cellRect)) + state.add(AccessibleState.SHOWING); + + // Figure out SELECTED state. + if (isCellSelected(row, column)) + state.add(AccessibleState.SELECTED); + + // Figure out ACTIVE state. + if (row == getSelectedRow() && column == getSelectedColumn()) + state.add(AccessibleState.ACTIVE); + + // TRANSIENT seems to be always set in the RI. + state.add(AccessibleState.TRANSIENT); + + // TODO: Any other state to handle here? + return state; } /** @@ -594,7 +616,463 @@ public class JTable return lastColumn; } } - + + /** + * The RI returns an instance with this name in + * {@link #getAccessibleColumnHeader()}, this makes sense, so we do the + * same. + */ + private class AccessibleTableHeader + implements AccessibleTable + { + + /** + * The JTableHeader wrapped by this class. + */ + private JTableHeader header; + + /** + * Creates a new instance. + * + * @param h the JTableHeader to wrap + */ + private AccessibleTableHeader(JTableHeader h) + { + header = h; + } + + /** + * Returns the caption for the table header. + * + * @return the caption for the table header + */ + public Accessible getAccessibleCaption() + { + // The RI seems to always return null here, so do we. + return null; + } + + /** + * Sets the caption for the table header. + * + * @param caption the caption to set + */ + public void setAccessibleCaption(Accessible caption) + { + // This seems to be a no-op in the RI, so we do the same. + } + + /** + * Returns the caption for the table header. + * + * @return the caption for the table header + */ + public Accessible getAccessibleSummary() + { + // The RI seems to always return null here, so do we. + return null; + } + + /** + * Sets the summary for the table header. + * + * @param summary the caption to set + */ + public void setAccessibleSummary(Accessible summary) + { + // This seems to be a no-op in the RI, so we do the same. + } + + /** + * Returns the number of rows, which is always 1 for the table header. + * + * @return the number of rows + */ + public int getAccessibleRowCount() + { + return 1; + } + + /** + * Returns the number of columns in the table header. + * + * @return the number of columns in the table header + */ + public int getAccessibleColumnCount() + { + return header.getColumnModel().getColumnCount(); + } + + /** + * Returns the accessible child at the specified row and column. + * The row number is ignored here, and we return an + * AccessibleJTableHeaderCell here with the renderer component as + * component. + * + * @param r the row number + * @param c the column number + * + * @return the accessible child at the specified row and column + */ + public Accessible getAccessibleAt(int r, int c) + { + TableColumn column = header.getColumnModel().getColumn(c); + TableCellRenderer rend = column.getHeaderRenderer(); + if (rend == null) + rend = header.getDefaultRenderer(); + Component comp = + rend.getTableCellRendererComponent(header.getTable(), + column.getHeaderValue(), false, + false, -1, c); + return new AccessibleJTableHeaderCell(header, comp, r, c); + } + + public int getAccessibleRowExtentAt(int r, int c) + { + // TODO Auto-generated method stub + return 0; + } + + public int getAccessibleColumnExtentAt(int r, int c) + { + // TODO Auto-generated method stub + return 0; + } + + public AccessibleTable getAccessibleRowHeader() + { + // TODO Auto-generated method stub + return null; + } + + public void setAccessibleRowHeader(AccessibleTable header) + { + // TODO Auto-generated method stub + + } + + public AccessibleTable getAccessibleColumnHeader() + { + // TODO Auto-generated method stub + return null; + } + + public void setAccessibleColumnHeader(AccessibleTable header) + { + // TODO Auto-generated method stub + + } + + public Accessible getAccessibleRowDescription(int r) + { + // TODO Auto-generated method stub + return null; + } + + public void setAccessibleRowDescription(int r, Accessible description) + { + // TODO Auto-generated method stub + + } + + public Accessible getAccessibleColumnDescription(int c) + { + // TODO Auto-generated method stub + return null; + } + + public void setAccessibleColumnDescription(int c, Accessible description) + { + // TODO Auto-generated method stub + + } + + public boolean isAccessibleSelected(int r, int c) + { + // TODO Auto-generated method stub + return false; + } + + public boolean isAccessibleRowSelected(int r) + { + // TODO Auto-generated method stub + return false; + } + + public boolean isAccessibleColumnSelected(int c) + { + // TODO Auto-generated method stub + return false; + } + + public int[] getSelectedAccessibleRows() + { + // TODO Auto-generated method stub + return null; + } + + public int[] getSelectedAccessibleColumns() + { + // TODO Auto-generated method stub + return null; + } + + } + + /** + * The RI returns an instance of such class for table header cells. This + * makes sense so I added this class. This still needs to be fully + * implemented, I just don't feel motivated enough to do so just now. + */ + private class AccessibleJTableHeaderCell + extends AccessibleContext + implements Accessible, AccessibleComponent + { + + private AccessibleJTableHeaderCell(JTableHeader h, Component comp, int r, + int c) + { + + } + + public AccessibleRole getAccessibleRole() + { + // TODO Auto-generated method stub + return null; + } + + public AccessibleStateSet getAccessibleStateSet() + { + // TODO Auto-generated method stub + return null; + } + + public int getAccessibleIndexInParent() + { + // TODO Auto-generated method stub + return 0; + } + + public int getAccessibleChildrenCount() + { + // TODO Auto-generated method stub + return 0; + } + + public Accessible getAccessibleChild(int i) + { + // TODO Auto-generated method stub + return null; + } + + public Locale getLocale() + { + // TODO Auto-generated method stub + return null; + } + + public AccessibleContext getAccessibleContext() + { + // TODO Auto-generated method stub + return null; + } + + public Color getBackground() + { + // TODO Auto-generated method stub + return null; + } + + public void setBackground(Color color) + { + // TODO Auto-generated method stub + + } + + public Color getForeground() + { + // TODO Auto-generated method stub + return null; + } + + public void setForeground(Color color) + { + // TODO Auto-generated method stub + + } + + public Cursor getCursor() + { + // TODO Auto-generated method stub + return null; + } + + public void setCursor(Cursor cursor) + { + // TODO Auto-generated method stub + + } + + public Font getFont() + { + // TODO Auto-generated method stub + return null; + } + + public void setFont(Font font) + { + // TODO Auto-generated method stub + + } + + public FontMetrics getFontMetrics(Font font) + { + // TODO Auto-generated method stub + return null; + } + + public boolean isEnabled() + { + // TODO Auto-generated method stub + return false; + } + + public void setEnabled(boolean b) + { + // TODO Auto-generated method stub + + } + + public boolean isVisible() + { + // TODO Auto-generated method stub + return false; + } + + public void setVisible(boolean b) + { + // TODO Auto-generated method stub + + } + + public boolean isShowing() + { + // TODO Auto-generated method stub + return false; + } + + public boolean contains(Point point) + { + // TODO Auto-generated method stub + return false; + } + + public Point getLocationOnScreen() + { + // TODO Auto-generated method stub + return null; + } + + public Point getLocation() + { + // TODO Auto-generated method stub + return null; + } + + public void setLocation(Point point) + { + // TODO Auto-generated method stub + + } + + public Rectangle getBounds() + { + // TODO Auto-generated method stub + return null; + } + + public void setBounds(Rectangle rectangle) + { + // TODO Auto-generated method stub + + } + + public Dimension getSize() + { + // TODO Auto-generated method stub + return null; + } + + public void setSize(Dimension dimension) + { + // TODO Auto-generated method stub + + } + + public Accessible getAccessibleAt(Point point) + { + // TODO Auto-generated method stub + return null; + } + + public boolean isFocusTraversable() + { + // TODO Auto-generated method stub + return false; + } + + public void requestFocus() + { + // TODO Auto-generated method stub + + } + + public void addFocusListener(FocusListener listener) + { + // TODO Auto-generated method stub + + } + + public void removeFocusListener(FocusListener listener) + { + // TODO Auto-generated method stub + + } + + } + + /** + * The last selected row. This is needed to track the selection in + * {@link #valueChanged(ListSelectionEvent)}. + */ + private int lastSelectedRow; + + /** + * The last selected column. This is needed to track the selection in + * {@link #valueChanged(ListSelectionEvent)}. + */ + private int lastSelectedColumn; + + /** + * The caption of the table. + */ + private Accessible caption; + + /** + * The summary of the table. + */ + private Accessible summary; + + /** + * Accessible descriptions for rows. + */ + private Accessible[] rowDescriptions; + + /** + * Accessible descriptions for columns. + */ + private Accessible[] columnDescriptions; + /** * Creates a new <code>AccessibleJTable</code>. * @@ -605,10 +1083,34 @@ public class JTable getModel().addTableModelListener(this); getSelectionModel().addListSelectionListener(this); getColumnModel().addColumnModelListener(this); - getCellEditor().addCellEditorListener(this); + lastSelectedRow = getSelectedRow(); + lastSelectedColumn = getSelectedColumn(); + TableCellEditor editor = getCellEditor(); + if (editor != null) + editor.addCellEditorListener(this); } /** + * Returns the accessible role for the <code>JTable</code> component. + * + * @return {@link AccessibleRole#TABLE}. + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.TABLE; + } + + /** + * Returns the accessible table. + * + * @return <code>this</code>. + */ + public AccessibleTable getAccessibleTable() + { + return this; + } + + /** * Returns the number of selected items in this table. */ public int getAccessibleSelectionCount() @@ -616,46 +1118,197 @@ public class JTable return getSelectedColumnCount(); } + /** + * Returns the selected accessible object with the specified index + * <code>i</code>. This basically returns the i-th selected cell in the + * table when going though it row-wise, and inside the rows, column-wise. + * + * @param i the index of the selected object to find + * + * @return the selected accessible object with the specified index + * <code>i</code> + */ public Accessible getAccessibleSelection(int i) { - // TODO Auto-generated method stub - return null; + Accessible found = null; + + int[] selectedRows = getSelectedRows(); + int[] selectedColumns = getSelectedColumns(); + int numCols = getColumnCount(); + int numRows = getRowCount(); + + // We have to go through every selected row and column and count until we + // find the specified index. This is potentially inefficient, but I can't + // think of anything better atm. + if (getRowSelectionAllowed() && getColumnSelectionAllowed()) + { + int current = -1; + int newIndex = current; + int lastSelectedRow = -1; + // Go through the selected rows array, don't forget the selected + // cells inside the not-selected rows' columns. + for (int j = 0; i < selectedRows.length; i++) + { + // Handle unselected rows between this selected and the last + // selected row, if any. + int selectedRow = selectedRows[j]; + int r = -1; + int ci = -1; + for (r = lastSelectedRow + 1; + r < selectedRow && current < i; r++) + { + for (ci = 0; ci < selectedColumns.length && current < i; + ci++) + { + current++; + } + } + if (current == i) + { + // We found the cell in the above loops, now get out of here. + found = getAccessibleChild(r * numCols + + selectedColumns[ci]); + break; + } + + // If we're still here, handle the current selected row. + if (current < i && current + numCols >= i) + { + // The cell must be in that row, which one is it? + found = getAccessibleChild(r * numCols + (i - current)); + break; + } + current += numCols; + } + if (found == null) + { + // The cell can still be in the last couple of unselected rows. + int r = 0; + int ci = 0; + for (r = lastSelectedRow + 1; + r < numRows && current < i; r++) + { + for (ci = 0; ci < selectedColumns.length && current < i; + ci++) + { + current++; + } + } + if (current == i) + { + // We found the cell in the above loops, now get out of here. + found = getAccessibleChild(r * numCols + + selectedColumns[ci]); + } + } + } + // One or more rows can be completely selected. + else if (getRowSelectionAllowed()) + { + int c = i % numCols; + int r = selectedRows[i / numCols]; + found = getAccessibleChild(r * numCols + c); + } + // One or more columns can be completely selected. + else if (getRowSelectionAllowed()) + { + int numSelectedColumns = selectedColumns.length; + int c = selectedColumns[i % numSelectedColumns]; + int r = i / numSelectedColumns; + found = getAccessibleChild(r * numCols + c); + } + + return found; } + /** + * Returns <code>true</code> if the accessible child with the index + * <code>i</code> is selected, <code>false</code> otherwise. + * + * @param i the index of the accessible to check + * + * @return <code>true</code> if the accessible child with the index + * <code>i</code> is selected, <code>false</code> otherwise + */ public boolean isAccessibleChildSelected(int i) { - // TODO Auto-generated method stub - return false; + int r = getAccessibleRowAtIndex(i); + int c = getAccessibleColumnAtIndex(i); + return isCellSelected(r, c); } + /** + * Adds the accessible child with the specified index <code>i</code> to the + * selection. + * + * @param i the index of the accessible child to add to the selection + */ public void addAccessibleSelection(int i) { - // TODO Auto-generated method stub - + int r = getAccessibleRowAtIndex(i); + int c = getAccessibleColumnAtIndex(i); + changeSelection(r, c, true, false); } + /** + * Removes the accessible child with the specified index <code>i</code> + * from the current selection. This will only work on tables that have + * cell selection enabled (<code>rowSelectionAllowed == false && + * columnSelectionAllowed == false</code>). + * + * @param i the index of the accessible to be removed from the selection + */ public void removeAccessibleSelection(int i) { - // TODO Auto-generated method stub - + if (! getRowSelectionAllowed() && ! getColumnSelectionAllowed()) + { + int r = getAccessibleRowAtIndex(i); + int c = getAccessibleColumnAtIndex(i); + removeRowSelectionInterval(r, r); + removeColumnSelectionInterval(c, c); + } } + /** + * Deselects all selected accessible children. + */ public void clearAccessibleSelection() { - // TODO Auto-generated method stub - + clearSelection(); } + /** + * Selects all accessible children that can be selected. This will only + * work on tables that support multiple selections and that have individual + * cell selection enabled. + */ public void selectAllAccessibleSelection() { - // TODO Auto-generated method stub - + selectAll(); } + /** + * Receives notification when the row selection changes and fires + * appropriate property change events. + * + * @param event the list selection event + */ public void valueChanged(ListSelectionEvent event) { - // TODO Auto-generated method stub - + firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY, + Boolean.FALSE, Boolean.TRUE); + int r = getSelectedRow(); + int c = getSelectedColumn(); + if (r != lastSelectedRow || c != lastSelectedColumn) + { + Accessible o = getAccessibleAt(lastSelectedRow, + lastSelectedColumn); + Accessible n = getAccessibleAt(r, c); + firePropertyChange(AccessibleContext + .ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY, o, n); + lastSelectedRow = r; + lastSelectedColumn = c; + } } /** @@ -680,14 +1333,13 @@ public class JTable /** * Receives notification when one or more rows have been inserted into the - * table. + * table and fires appropriate property change events. * * @param event the table model event */ public void tableRowsInserted(TableModelEvent event) { - // TODO: What to do here, if anything? This might be a hook method for - // subclasses... + handleRowChange(event); } /** @@ -698,50 +1350,93 @@ public class JTable */ public void tableRowsDeleted(TableModelEvent event) { - // TODO: What to do here, if anything? This might be a hook method for - // subclasses... + handleRowChange(event); + } + + /** + * Fires a PropertyChangeEvent for inserted or deleted rows. + * + * @param event the table model event + */ + private void handleRowChange(TableModelEvent event) + { + firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, + null, null); + int firstColumn = event.getColumn(); + int lastColumn = event.getColumn(); + if (firstColumn == TableModelEvent.ALL_COLUMNS) + { + firstColumn = 0; + lastColumn = getColumnCount() - 1; + } + AccessibleJTableModelChange change = new AccessibleJTableModelChange + (event.getType(), event.getFirstRow(), event.getLastRow(), + firstColumn, lastColumn); + firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED, + null, change); } public void columnAdded(TableColumnModelEvent event) { - // TODO Auto-generated method stub - + firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, + null, null); + handleColumnChange(AccessibleTableModelChange.INSERT, + event.getFromIndex(), event.getToIndex()); } - public void columnMarginChanged(ChangeEvent event) + public void columnRemoved(TableColumnModelEvent event) { - // TODO Auto-generated method stub - + firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, + null, null); + handleColumnChange(AccessibleTableModelChange.DELETE, + event.getFromIndex(), event.getToIndex()); } public void columnMoved(TableColumnModelEvent event) { - // TODO Auto-generated method stub - + firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, + null, null); + handleColumnChange(AccessibleTableModelChange.DELETE, + event.getFromIndex(), event.getFromIndex()); + handleColumnChange(AccessibleTableModelChange.INSERT, + event.getFromIndex(), event.getToIndex()); } - public void columnRemoved(TableColumnModelEvent event) + /** + * Fires a PropertyChangeEvent for inserted or deleted columns. + * + * @param type the type of change + * @param from the start of the change + * @param to the target of the change + */ + private void handleColumnChange(int type, int from, int to) { - // TODO Auto-generated method stub - + AccessibleJTableModelChange change = + new AccessibleJTableModelChange(type, 0, 0, from, to); + firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED, + null, change); + } + + public void columnMarginChanged(ChangeEvent event) + { + firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, + null, null); } public void columnSelectionChanged(ListSelectionEvent event) { - // TODO Auto-generated method stub - + // AFAICS, nothing is done here. } public void editingCanceled(ChangeEvent event) { - // TODO Auto-generated method stub - + // AFAICS, nothing is done here. } public void editingStopped(ChangeEvent event) { - // TODO Auto-generated method stub - + firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, + null, null); } /** @@ -784,154 +1479,324 @@ public class JTable } } + /** + * Returns the row number of an accessible child (cell) with the specified + * index. + * + * @param index the index of the cell of which the row number is queried + * + * @return the row number of an accessible child (cell) with the specified + * index + */ public int getAccessibleRow(int index) { - // TODO Auto-generated method stub - return 0; + return getAccessibleRowAtIndex(index); } + /** + * Returns the column number of an accessible child (cell) with the + * specified index. + * + * @param index the index of the cell of which the column number is queried + * + * @return the column number of an accessible child (cell) with the + * specified index + */ public int getAccessibleColumn(int index) { - // TODO Auto-generated method stub - return 0; + return getAccessibleColumnAtIndex(index); } + /** + * Returns the index of the accessible child at the specified row and + * column. + * + * @param r the row number + * @param c the column number + * + * @return the index of the accessible child at the specified row and + * column + */ public int getAccessibleIndex(int r, int c) { - // TODO Auto-generated method stub - return 0; + return getAccessibleIndexAt(r, c); } + /** + * Returns the caption of the table. + * + * @return the caption of the table + * + * @see #setAccessibleCaption(Accessible) + */ public Accessible getAccessibleCaption() { - // TODO Auto-generated method stub - return null; + return caption; } - public void setAccessibleCaption(Accessible caption) + /** + * Sets the caption for the table. + * + * @param c the caption to set + */ + public void setAccessibleCaption(Accessible c) { - // TODO Auto-generated method stub - + caption = c; } + /** + * Returns the summary for the table. + * + * @return the summary for the table + */ public Accessible getAccessibleSummary() { - // TODO Auto-generated method stub - return null; + return summary; } - public void setAccessibleSummary(Accessible summary) + /** + * Sets the summary for the table. + * + * @param s the summary to set + */ + public void setAccessibleSummary(Accessible s) { - // TODO Auto-generated method stub - + summary = s; } + /** + * Returns the number of rows in the table. + * + * @return the number of rows in the table + */ public int getAccessibleRowCount() { - // TODO Auto-generated method stub - return 0; + return getRowCount(); } + /** + * Returns the number of columns in the table. + * + * @return the number of columns in the table + */ public int getAccessibleColumnCount() { - // TODO Auto-generated method stub - return 0; + return getColumnCount(); } + /** + * Returns the accessible child (table cell) at the specified row and + * column. + * + * @param r the row number + * @param c the column number + * + * @return the accessible child (table cell) at the specified row and + * column + */ public Accessible getAccessibleAt(int r, int c) { - // TODO Auto-generated method stub - return null; + return getAccessibleChild(r * getAccessibleColumnCount() + c); } + /** + * Returns the number of rows that the specified cell occupies. The + * standard table cells only occupy one row, so we return <code>1</code> + * here. + * + * @param r the row number + * @param c the column number + * + * @return the number of rows that the specified cell occupies + */ public int getAccessibleRowExtentAt(int r, int c) { - // TODO Auto-generated method stub - return 0; + return 1; } + /** + * Returns the number of columns that the specified cell occupies. The + * standard table cells only occupy one column, so we return <code>1</code> + * here. + * + * @param r the row number + * @param c the column number + * + * @return the number of rows that the specified cell occupies + */ public int getAccessibleColumnExtentAt(int r, int c) { - // TODO Auto-generated method stub - return 0; + return 1; } + /** + * Returns the accessible row header. + * + * @return the accessible row header + */ public AccessibleTable getAccessibleRowHeader() { - // TODO Auto-generated method stub + // The RI seems to always return null here, so do we. return null; } + /** + * Sets the accessible row header. + * + * @param header the header to set + */ public void setAccessibleRowHeader(AccessibleTable header) { - // TODO Auto-generated method stub - + // In the RI this seems to be a no-op. } + /** + * Returns the column header. + * + * @return the column header, or <code>null</code> if there is no column + * header + */ public AccessibleTable getAccessibleColumnHeader() { - // TODO Auto-generated method stub - return null; + JTableHeader h = getTableHeader(); + AccessibleTable header = null; + if (h != null) + header = new AccessibleTableHeader(h); + return header; } + /** + * Sets the accessible column header. The default implementation doesn't + * allow changing the header this way, so this is a no-op. + * + * @param header the accessible column header to set + */ public void setAccessibleColumnHeader(AccessibleTable header) { - // TODO Auto-generated method stub - + // The RI doesn't seem to do anything, so we also do nothing. } + /** + * Returns the accessible description for the row with the specified index, + * or <code>null</code> if no description has been set. + * + * @param r the row for which the description is queried + * + * @return the accessible description for the row with the specified index, + * or <code>null</code> if no description has been set + */ public Accessible getAccessibleRowDescription(int r) { - // TODO Auto-generated method stub - return null; + Accessible descr = null; + if (rowDescriptions != null) + descr = rowDescriptions[r]; + return descr; } + /** + * Sets the accessible description for the row with the specified index. + * + * @param r the row number for which to set the description + * @param description the description to set + */ public void setAccessibleRowDescription(int r, Accessible description) { - // TODO Auto-generated method stub - + if (rowDescriptions == null) + rowDescriptions = new Accessible[getAccessibleRowCount()]; + rowDescriptions[r] = description; } + /** + * Returns the accessible description for the column with the specified + * index, or <code>null</code> if no description has been set. + * + * @param c the column for which the description is queried + * + * @return the accessible description for the column with the specified + * index, or <code>null</code> if no description has been set + */ public Accessible getAccessibleColumnDescription(int c) { - // TODO Auto-generated method stub - return null; + Accessible descr = null; + if (columnDescriptions != null) + descr = columnDescriptions[c]; + return descr; } + /** + * Sets the accessible description for the column with the specified index. + * + * @param c the column number for which to set the description + * @param description the description to set + */ public void setAccessibleColumnDescription(int c, Accessible description) { - // TODO Auto-generated method stub - + if (columnDescriptions == null) + columnDescriptions = new Accessible[getAccessibleRowCount()]; + columnDescriptions[c] = description; } + /** + * Returns <code>true</code> if the accessible child at the specified + * row and column is selected, <code>false</code> otherwise. + * + * @param r the row number of the child + * @param c the column number of the child + * + * @return <code>true</code> if the accessible child at the specified + * row and column is selected, <code>false</code> otherwise + */ public boolean isAccessibleSelected(int r, int c) { - // TODO Auto-generated method stub - return false; + return isCellSelected(r, c); } + /** + * Returns <code>true</code> if the row with the specified index is + * selected, <code>false</code> otherwise. + * + * @param r the row number + * + * @return <code>true</code> if the row with the specified index is + * selected, <code>false</code> otherwise + */ public boolean isAccessibleRowSelected(int r) { - // TODO Auto-generated method stub - return false; + return isRowSelected(r); } + /** + * Returns <code>true</code> if the column with the specified index is + * selected, <code>false</code> otherwise. + * + * @param c the column number + * + * @return <code>true</code> if the column with the specified index is + * selected, <code>false</code> otherwise + */ public boolean isAccessibleColumnSelected(int c) { - // TODO Auto-generated method stub - return false; + return isColumnSelected(c); } + /** + * Returns the indices of all selected rows. + * + * @return the indices of all selected rows + */ public int[] getSelectedAccessibleRows() { - // TODO Auto-generated method stub - return null; + return getSelectedRows(); } + /** + * Returns the indices of all selected columns. + * + * @return the indices of all selected columns + */ public int[] getSelectedAccessibleColumns() { - // TODO Auto-generated method stub - return null; + return getSelectedColumns(); } /** @@ -1017,7 +1882,17 @@ public class JTable /** * The CheckBox that is used for rendering. */ - private final JCheckBox checkBox = new JCheckBox(); + private final JCheckBox checkBox; + + /** + * Creates a new checkbox based boolean cell renderer. The checkbox is + * centered by default. + */ + BooleanCellRenderer() + { + checkBox = new JCheckBox(); + checkBox.setHorizontalAlignment(SwingConstants.CENTER); + } /** * Get the check box. @@ -1223,6 +2098,12 @@ public class JTable private class IconCellRenderer extends DefaultTableCellRenderer { + IconCellRenderer() + { + setHorizontalAlignment(SwingConstants.CENTER); + } + + /** * Returns the component that is used for rendering the value. * @@ -1272,17 +2153,6 @@ public class JTable { setBorder(BorderFactory.createLineBorder(getGridColor(), 2)); } - - /** - * With not this method overridden, the scroll pane scrolls to the - * top left cornec (untranslated position of the caret) after the first - * keystroke. - */ - public void scrollRectToVisible(Rectangle r) - { - // Do nothing here. If the editing session starts outside the visible - // bounds, the editCellAt will scroll. - } } @@ -1335,14 +2205,14 @@ public class JTable * {@link TableCellEditor} objects. This table is consulted by the * FIXME */ - protected Hashtable defaultEditorsByColumnClass; + protected Hashtable defaultEditorsByColumnClass = new Hashtable(); /** * A table mapping {@link java.lang.Class} objects to * {@link TableCellEditor} objects. This table is consulted by the * FIXME */ - protected Hashtable defaultRenderersByColumnClass; + protected Hashtable defaultRenderersByColumnClass = new Hashtable(); /** * The column that is edited, -1 if the table is not edited currently. @@ -1588,6 +2458,27 @@ public class JTable private boolean clientRowHeightSet = false; /** + * Stores the sizes and positions of each row, when using non-uniform row + * heights. Initially the height of all rows is equal and stored in + * {link #rowHeight}. However, when an application calls + * {@link #setRowHeight(int,int)}, the table switches to non-uniform + * row height mode which stores the row heights in the SizeSequence + * object instead. + * + * @see #setRowHeight(int) + * @see #getRowHeight() + * @see #getRowHeight(int) + * @see #setRowHeight(int, int) + */ + private SizeSequence rowHeights; + + /** + * This editor serves just a marker that the value must be simply changed to + * the opposite one instead of starting the editing session. + */ + private transient TableCellEditor booleanInvertingEditor; + + /** * Creates a new <code>JTable</code> instance. */ public JTable () @@ -1719,15 +2610,9 @@ public class JTable if (autoCreateColumnsFromModel) createDefaultColumnsFromModel(); this.columnModel.addColumnModelListener(this); - - this.defaultRenderersByColumnClass = new Hashtable(); - createDefaultRenderers(); - - this.defaultEditorsByColumnClass = new Hashtable(); - createDefaultEditors(); this.autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS; - this.rowHeight = 16; + setRowHeight(16); this.rowMargin = 1; this.rowSelectionAllowed = true; // this.accessibleContext = new AccessibleJTable(); @@ -1773,7 +2658,10 @@ public class JTable protected void createDefaultEditors() { JCheckBox box = new BooleanCellRenderer().getCheckBox(); - setDefaultEditor(Boolean.class, new DefaultCellEditor(box)); + box.setBorder(BorderFactory.createLineBorder(getGridColor(), 2)); + box.setBorderPainted(true); + booleanInvertingEditor = new DefaultCellEditor(box); + setDefaultEditor(Boolean.class, booleanInvertingEditor); } /** @@ -1871,6 +2759,8 @@ public class JTable */ public void columnMoved (TableColumnModelEvent event) { + if (isEditing()) + editingCanceled(null); revalidate(); repaint(); } @@ -1891,12 +2781,6 @@ public class JTable */ public void columnSelectionChanged (ListSelectionEvent event) { - // Does not make sense for the table with the single column. - if (getColumnCount() < 2) - return; - - int x0 = 0; - // We must limit the indices to the bounds of the JTable's model, because // we might get values of -1 or greater then columnCount in the case // when columns get removed. @@ -1904,17 +2788,39 @@ public class JTable event.getFirstIndex())); int idxn = Math.max(0, Math.min(getColumnCount() - 1, event.getLastIndex())); - int i; - for (i = 0; i < idx0; i++) - x0 += columnModel.getColumn(i).getWidth(); - - int xn = x0; - - for (i = idx0; i <= idxn; i++) - xn += columnModel.getColumn(i).getWidth(); - - repaint(x0, 0, xn-x0, getHeight()); + int minRow = 0; + int maxRow = getRowCount() - 1; + if (getRowSelectionAllowed()) + { + minRow = selectionModel.getMinSelectionIndex(); + maxRow = selectionModel.getMaxSelectionIndex(); + int leadRow = selectionModel.getLeadSelectionIndex(); + if (minRow == -1 && maxRow == -1) + { + minRow = leadRow; + maxRow = leadRow; + } + else + { + // In this case we need to repaint also the range to leadRow, not + // only between min and max. + if (leadRow != -1) + { + minRow = Math.min(minRow, leadRow); + maxRow = Math.max(maxRow, leadRow); + } + } + } + if (minRow != -1 && maxRow != -1) + { + Rectangle first = getCellRect(minRow, idx0, false); + Rectangle last = getCellRect(maxRow, idxn, false); + Rectangle dirty = SwingUtilities.computeUnion(first.x, first.y, + first.width, + first.height, last); + repaint(dirty); + } } /** @@ -1958,7 +2864,13 @@ public class JTable // changed and the flag autoCreateColumnsFromModel is set if ((event == null || (event.getFirstRow() == TableModelEvent.HEADER_ROW)) && autoCreateColumnsFromModel) - createDefaultColumnsFromModel(); + { + rowHeights = null; + if (getAutoCreateColumnsFromModel()) + createDefaultColumnsFromModel(); + resizeAndRepaint(); + return; + } // If the structure changes, we need to revalidate, since that might // affect the size parameters of the JTable. Otherwise we only need @@ -1975,6 +2887,8 @@ public class JTable if (last < 0) last = getRowCount() - 1; selectionModel.insertIndexInterval(first, last - first + 1, true); + if (rowHeights != null) + rowHeights.insertEntries(first, last - first + 1, rowHeight); } revalidate(); } @@ -1990,6 +2904,8 @@ public class JTable if (last < 0) last = getRowCount() - 1; selectionModel.removeIndexInterval(first, last); + if (rowHeights != null) + rowHeights.removeEntries(first, last - first + 1); } if (dataModel.getRowCount() == 0) clearSelection(); @@ -2004,14 +2920,19 @@ public class JTable */ public void valueChanged (ListSelectionEvent event) { - // Does not make sense for the table with the single row. - if (getRowCount() < 2) - return; + // If we are in the editing process, end the editing session. + if (isEditing()) + editingStopped(null); - int y_gap = rowMargin; - int y0 = (getRowHeight() + y_gap) * (event.getFirstIndex()); - int yn = (getRowHeight() + y_gap) * (event.getLastIndex()+1); - repaint(0, y0, getWidth(), yn-y0); + // Repaint the changed region. + int first = Math.max(0, Math.min(getRowCount() - 1, event.getFirstIndex())); + int last = Math.max(0, Math.min(getRowCount() - 1, event.getLastIndex())); + Rectangle rect1 = getCellRect(first, 0, false); + Rectangle rect2 = getCellRect(last, getColumnCount() - 1, false); + Rectangle dirty = SwingUtilities.computeUnion(rect2.x, rect2.y, + rect2.width, rect2.height, + rect1); + repaint(dirty); } /** @@ -2053,10 +2974,16 @@ public class JTable if (point != null) { int nrows = getRowCount(); - int height = getRowHeight() + getRowMargin(); + int r; int y = point.y; + if (rowHeights == null) + { + int height = getRowHeight(); + r = y / height; + } + else + r = rowHeights.getIndex(y); - int r = y / height; if (r < 0 || r >= nrows) return -1; else @@ -2086,27 +3013,70 @@ public class JTable int column, boolean includeSpacing) { - int height = getRowHeight(row); - int width = columnModel.getColumn(column).getWidth(); - int x_gap = columnModel.getColumnMargin(); - int y_gap = rowMargin; + Rectangle cellRect = new Rectangle(0, 0, 0, 0); - column = Math.max(0, Math.min(column, getColumnCount() - 1)); - row = Math.max(0, Math.min(row, getRowCount() - 1)); - - int x = 0; - int y = (height + y_gap) * row; + // Check for valid range vertically. + if (row >= getRowCount()) + { + cellRect.height = getHeight(); + } + else if (row >= 0) + { + cellRect.height = getRowHeight(row); + if (rowHeights == null) + cellRect.y = row * cellRect.height; + else + cellRect.y = rowHeights.getPosition(row); - for (int i = 0; i < column; ++i) - x += columnModel.getColumn(i).getWidth(); - - Rectangle rect = new Rectangle(); + if (! includeSpacing) + { + // The rounding here is important. + int rMargin = getRowMargin(); + cellRect.y += rMargin / 2; + cellRect.height -= rMargin; + } + } + // else row < 0, y = height = 0 - if (includeSpacing) - rect.setBounds(x, y, width, height +y_gap); + // Check for valid range horizontally. + if (column < 0) + { + if (! getComponentOrientation().isLeftToRight()) + { + cellRect.x = getWidth(); + } + } + else if (column >= getColumnCount()) + { + if (getComponentOrientation().isLeftToRight()) + { + cellRect.x = getWidth(); + } + } else - rect.setBounds(x, y, width - x_gap, height); - return rect; + { + TableColumnModel tcm = getColumnModel(); + if (getComponentOrientation().isLeftToRight()) + { + for (int i = 0; i < column; i++) + cellRect.x += tcm.getColumn(i).getWidth(); + } + else + { + for (int i = tcm.getColumnCount() - 1; i > column; i--) + cellRect.x += tcm.getColumn(i).getWidth(); + } + cellRect.width = tcm.getColumn(column).getWidth(); + if (! includeSpacing) + { + // The rounding here is important. + int cMargin = tcm.getColumnMargin(); + cellRect.x += cMargin / 2; + cellRect.width -= cMargin; + } + } + + return cellRect; } public void clearSelection() @@ -2354,7 +3324,6 @@ public class JTable int row, int column) { - boolean rowSelAllowed = getRowSelectionAllowed(); boolean colSelAllowed = getColumnSelectionAllowed(); boolean isSel = false; @@ -2418,9 +3387,10 @@ public class JTable */ public int getRowHeight(int row) { - // FIXME: return the height of the specified row - // which may be different from the general rowHeight - return rowHeight; + int rh = rowHeight; + if (rowHeights != null) + rh = rowHeights.getSize(row); + return rh; } @@ -2626,6 +3596,17 @@ public class JTable */ public AccessibleContext getAccessibleContext() { + if (accessibleContext == null) + { + AccessibleJTable ctx = new AccessibleJTable(); + addPropertyChangeListener(ctx); + TableColumnModel tcm = getColumnModel(); + tcm.addColumnModelListener(ctx); + tcm.getSelectionModel().addListSelectionListener(ctx); + getSelectionModel().addListSelectionListener(ctx); + + accessibleContext = ctx; + } return accessibleContext; } @@ -2780,9 +3761,14 @@ public class JTable } /** - * Set the value of the {@link #rowHeight} property. + * Sets the height for all rows in the table. If you want to change the + * height of a single row instead, use {@link #setRowHeight(int, int)}. * - * @param r The new value of the rowHeight property + * @param r the height to set for all rows + * + * @see #getRowHeight() + * @see #setRowHeight(int, int) + * @see #getRowHeight(int) */ public void setRowHeight(int r) { @@ -2792,21 +3778,24 @@ public class JTable clientRowHeightSet = true; rowHeight = r; + rowHeights = null; revalidate(); repaint(); } /** - * Sets the value of the rowHeight property for the specified - * row. + * Sets the height of a single row in the table. * - * @param rh is the new rowHeight - * @param row is the row to change the rowHeight of + * @param rh the new row height + * @param row the row to change the height of */ public void setRowHeight(int row, int rh) { - setRowHeight(rh); - // FIXME: not implemented + if (rowHeights == null) + { + rowHeights = new SizeSequence(getRowCount(), rowHeight); + } + rowHeights.setSize(row, rh); } /** @@ -2878,6 +3867,10 @@ public class JTable // Add table as TableModelListener to new model. dataModel.addTableModelListener(this); + // Notify the tableChanged method. + tableChanged(new TableModelEvent(dataModel, + TableModelEvent.HEADER_ROW)); + // Automatically create columns. if (autoCreateColumnsFromModel) createDefaultColumnsFromModel(); @@ -3210,7 +4203,7 @@ public class JTable public void doLayout() { TableColumn resizingColumn = null; - + int ncols = getColumnCount(); if (ncols < 1) return; @@ -3236,7 +4229,7 @@ public class JTable { TableColumn col; TableColumn [] cols; - + switch (getAutoResizeMode()) { case AUTO_RESIZE_LAST_COLUMN: @@ -3298,20 +4291,44 @@ public class JTable TableColumn [] cols = new TableColumn[ncols]; for (int i = 0; i < ncols; ++i) cols[i] = columnModel.getColumn(i); - distributeSpill(cols, spill); + distributeSpill(cols, spill); } if (editorComp!=null) moveToCellBeingEdited(editorComp); - // Repaint fixes the invalid view after the first keystroke if the cell - // editing is started immediately after the program start or cell - // resizing. - repaint(); - if (tableHeader!=null) - tableHeader.repaint(); + int leftBoundary = getLeftResizingBoundary(); + int width = getWidth() - leftBoundary; + repaint(leftBoundary, 0, width, getHeight()); + if (tableHeader != null) + tableHeader.repaint(leftBoundary, 0, width, tableHeader.getHeight()); + } + + /** + * Get the left boundary of the rectangle which changes during the column + * resizing. + */ + int getLeftResizingBoundary() + { + if (tableHeader == null || getAutoResizeMode() == AUTO_RESIZE_ALL_COLUMNS) + return 0; + else + { + TableColumn resizingColumn = tableHeader.getResizingColumn(); + if (resizingColumn == null) + return 0; + + int rc = convertColumnIndexToView(resizingColumn.getModelIndex()); + int p = 0; + + for (int i = 0; i < rc; i++) + p += columnModel.getColumn(i).getWidth(); + + return p; + } } + /** * @deprecated Replaced by <code>doLayout()</code> */ @@ -3351,6 +4368,10 @@ public class JTable public void setUI(TableUI ui) { super.setUI(ui); + // The editors and renderers must be recreated because they constructors + // may use the look and feel properties. + createDefaultEditors(); + createDefaultRenderers(); } public void updateUI() @@ -3567,6 +4588,10 @@ public class JTable */ public void selectAll() { + // The table is empty - nothing to do! + if (getRowCount() == 0 || getColumnCount() == 0) + return; + // rowLead and colLead store the current lead selection indices int rowLead = selectionModel.getLeadSelectionIndex(); int colLead = getColumnModel().getSelectionModel().getLeadSelectionIndex(); @@ -3716,30 +4741,47 @@ public class JTable /** * Programmatically starts editing the specified cell. - * + * * @param row the row of the cell to edit. * @param column the column of the cell to edit. */ - public boolean editCellAt (int row, int column) + public boolean editCellAt(int row, int column) { // Complete the previous editing session, if still active. if (isEditing()) editingStopped(new ChangeEvent("editingStopped")); - - editingRow = row; - editingColumn = column; - setCellEditor(getCellEditor(row, column)); - editorComp = prepareEditor(cellEditor, row, column); + TableCellEditor editor = getCellEditor(row, column); - // Remove the previous editor components, if present. Only one - // editor component at time is allowed in the table. - removeAll(); - add(editorComp); - moveToCellBeingEdited(editorComp); - scrollRectToVisible(editorComp.getBounds()); - editorComp.requestFocusInWindow(); - return true; + // The boolean values are inverted by the single click without the + // real editing session. + if (editor == booleanInvertingEditor && isCellEditable(row, column)) + { + if (Boolean.TRUE.equals(getValueAt(row, column))) + setValueAt(Boolean.FALSE, row, column); + else + setValueAt(Boolean.TRUE, row, column); + return false; + } + else + { + editingRow = row; + editingColumn = column; + + setCellEditor(editor); + editorComp = prepareEditor(cellEditor, row, column); + + // Remove the previous editor components, if present. Only one + // editor component at time is allowed in the table. + removeAll(); + add(editorComp); + moveToCellBeingEdited(editorComp); + scrollRectToVisible(editorComp.getBounds()); + editorComp.requestFocusInWindow(); + + // Deliver the should select event. + return editor.shouldSelectCell(null); + } } /** |