From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- libjava/classpath/javax/swing/JTable.java | 5157 +++++++++++++++++++++++++++++ 1 file changed, 5157 insertions(+) create mode 100644 libjava/classpath/javax/swing/JTable.java (limited to 'libjava/classpath/javax/swing/JTable.java') diff --git a/libjava/classpath/javax/swing/JTable.java b/libjava/classpath/javax/swing/JTable.java new file mode 100644 index 000000000..b60c67aa3 --- /dev/null +++ b/libjava/classpath/javax/swing/JTable.java @@ -0,0 +1,5157 @@ +/* JTable.java -- + Copyright (C) 2002, 2004, 2005, 2006 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.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.text.DateFormat; +import java.text.NumberFormat; +import java.util.Date; +import java.util.EventObject; +import java.util.Hashtable; +import java.util.Locale; +import java.util.Vector; + +import javax.accessibility.Accessible; +import javax.accessibility.AccessibleComponent; +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; +import javax.swing.event.CellEditorListener; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.event.TableColumnModelEvent; +import javax.swing.event.TableColumnModelListener; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; +import javax.swing.plaf.TableUI; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.DefaultTableColumnModel; +import javax.swing.table.DefaultTableModel; +import javax.swing.table.JTableHeader; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableColumnModel; +import javax.swing.table.TableModel; + +/** + * The table component, displaying information, organized in rows and columns. + * The table can be placed in the scroll bar and have the optional header + * that is always visible. Cell values may be editable after double clicking + * on the cell. Cell columns may have various data types, that are + * displayed and edited by the different renderers and editors. It is possible + * to set different column width. The columns are also resizeable by + * dragging the column boundary in the header. + */ +public class JTable + extends JComponent + implements TableModelListener, Scrollable, TableColumnModelListener, + ListSelectionListener, CellEditorListener, Accessible +{ + /** + * Provides accessibility support for JTable. + * + * @author Roman Kennke (kennke@aicas.com) + */ + protected class AccessibleJTable + extends AccessibleJComponent + implements AccessibleSelection, ListSelectionListener, TableModelListener, + TableColumnModelListener, CellEditorListener, PropertyChangeListener, + AccessibleExtendedTable + { + + /** + * Provides accessibility support for table cells. + * + * @author Roman Kennke (kennke@aicas.com) + */ + protected class AccessibleJTableCell + extends AccessibleContext + implements Accessible, AccessibleComponent + { + + /** + * The table of this cell. + */ + private JTable table; + + /** + * The row index of this cell. + */ + private int row; + + /** + * The column index of this cell. + */ + private int column; + + /** + * The index of this cell inside the AccessibleJTable parent. + */ + private int index; + + /** + * Creates a new AccessibleJTableCell. + * + * @param t the table + * @param r the row + * @param c the column + * @param i the index of this cell inside the accessible table parent + */ + public AccessibleJTableCell(JTable t, int r, int c, int i) + { + table = t; + row = r; + column = c; + index = i; + } + + /** + * Returns the accessible row for the table cell. + * + * @return the accessible row for the table cell + */ + 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; + } + + /** + * Returns the accessible state set of this accessible table cell. + * + * @return the accessible state set of this accessible table cell + */ + public AccessibleStateSet getAccessibleStateSet() + { + 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; + } + + /** + * Returns the index of this cell in the parent object. + * + * @return the index of this cell in the parent object + */ + public int getAccessibleIndexInParent() + { + return index; + } + + /** + * Returns the number of children of this object. Table cells cannot have + * children, so we return 0 here. + * + * @return 0 + */ + public int getAccessibleChildrenCount() + { + return 0; + } + + /** + * Returns the accessible child at index i. Table cells + * don't have children, so we return null here. + * + * @return null + */ + public Accessible getAccessibleChild(int i) + { + return null; + } + + /** + * Returns the locale setting for this accessible table cell. + * + * @return the locale setting for this accessible table cell + */ + public Locale getLocale() + { + // TODO: For now, we return english here. This must be fixed as soon + // as we have a localized Swing. + return Locale.ENGLISH; + } + + /** + * Returns the accessible context of this table cell. Since accessible + * table cells are their own accessible context, we return + * this. + * + * @return the accessible context of this table cell + */ + public AccessibleContext getAccessibleContext() + { + return this; + } + + /** + * Returns the background color of this cell. + * + * @return the background color of this cell + */ + public Color getBackground() + { + return table.getBackground(); + } + + /** + * Sets the background of the cell. Since table cells cannot have + * individual background colors, this method does nothing. Set the + * background directly on the table instead. + * + * @param color not used + */ + public void setBackground(Color color) + { + // This method does nothing. See API comments. + } + + /** + * Returns the foreground color of the table cell. + * + * @return the foreground color of the table cell + */ + public Color getForeground() + { + return table.getForeground(); + } + + /** + * Sets the foreground of the cell. Since table cells cannot have + * individual foreground colors, this method does nothing. Set the + * foreground directly on the table instead. + * + * @param color not used + */ + public void setForeground(Color color) + { + // This method does nothing. See API comments. + } + + /** + * Returns the cursor for this table cell. + * + * @return the cursor for this table cell + */ + public Cursor getCursor() + { + return table.getCursor(); + } + + /** + * Sets the cursor of the cell. Since table cells cannot have + * individual cursors, this method does nothing. Set the + * cursor directly on the table instead. + * + * @param cursor not used + */ + public void setCursor(Cursor cursor) + { + // This method does nothing. See API comments. + } + + /** + * Returns the font of the table cell. + * + * @return the font of the table cell + */ + public Font getFont() + { + return table.getFont(); + } + + /** + * Sets the font of the cell. Since table cells cannot have + * individual fonts, this method does nothing. Set the + * font directly on the table instead. + * + * @param font not used + */ + public void setFont(Font font) + { + // This method does nothing. See API comments. + } + + /** + * Returns the font metrics for a specified font. + * + * @param font the font for which we return the metrics + * + * @return the font metrics for a specified font + */ + public FontMetrics getFontMetrics(Font font) + { + return table.getFontMetrics(font); + } + + /** + * Returns true if this table cell is enabled, + * false otherwise. + * + * @return true if this table cell is enabled, + * false otherwise + */ + public boolean isEnabled() + { + return table.isEnabled(); + } + + /** + * Table cells cannot be disabled or enabled individually, so this method + * does nothing. Set the enabled flag on the table itself. + * + * @param b not used here + */ + public void setEnabled(boolean b) + { + // This method does nothing. See API comments. + } + + /** + * Returns true if this cell is visible, false + * otherwise. + * + * @return true if this cell is visible, false + * otherwise + */ + public boolean isVisible() + { + return table.isVisible(); + } + + /** + * The visibility cannot be set on individual table cells, so this method + * does nothing. Set the visibility on the table itself. + * + * @param b not used + */ + public void setVisible(boolean b) + { + // This method does nothing. See API comments. + } + + /** + * Returns true if this table cell is currently showing on + * screen. + * + * @return true if this table cell is currently showing on + * screen + */ + public boolean isShowing() + { + return table.isShowing(); + } + + /** + * Returns true if this table cell contains the location + * at point, false otherwise. + * point is interpreted as relative to the coordinate system + * of the table cell. + * + * @return true if this table cell contains the location + * at point, false otherwise + */ + public boolean contains(Point point) + { + Rectangle cellRect = table.getCellRect(row, column, true); + cellRect.x = 0; + cellRect.y = 0; + return cellRect.contains(point); + } + + /** + * Returns the screen location of the table cell. + * + * @return the screen location of the table cell + */ + public Point getLocationOnScreen() + { + Point tableLoc = table.getLocationOnScreen(); + Rectangle cellRect = table.getCellRect(row, column, true); + tableLoc.x += cellRect.x; + tableLoc.y += cellRect.y; + return tableLoc; + } + + /** + * Returns the location of this cell relative to the table's bounds. + * + * @return the location of this cell relative to the table's bounds + */ + public Point getLocation() + { + Rectangle cellRect = table.getCellRect(row, column, true); + return new Point(cellRect.x, cellRect.y); + } + + /** + * The location of the table cells cannot be manipulated directly, so + * this method does nothing. + * + * @param point not used + */ + public void setLocation(Point point) + { + // This method does nothing. See API comments. + } + + /** + * Returns the bounds of the cell relative to its table. + * + * @return the bounds of the cell relative to its table + */ + public Rectangle getBounds() + { + return table.getCellRect(row, column, true); + } + + /** + * The bounds of the table cells cannot be manipulated directly, so + * this method does nothing. + * + * @param rectangle not used + */ + public void setBounds(Rectangle rectangle) + { + // This method does nothing. See API comments. + } + + /** + * Returns the size of the table cell. + * + * @return the size of the table cell + */ + public Dimension getSize() + { + Rectangle cellRect = table.getCellRect(row, column, true); + return new Dimension(cellRect.width, cellRect.height); + } + + /** + * The size cannot be set on table cells directly, so this method does + * nothing. + * + * @param dimension not used + */ + public void setSize(Dimension dimension) + { + // This method does nothing. See API comments. + } + + /** + * Table cells have no children, so we return null here. + * + * @return null + */ + public Accessible getAccessibleAt(Point point) + { + return null; + } + + /** + * Returns true if this table cell is focus traversable, + * false otherwise. + * + * @return true if this table cell is focus traversable, + * false otherwise + */ + public boolean isFocusTraversable() + { + return table.isFocusable(); + } + + /** + * Requests that this table cell gets the keyboard focus. + */ + public void requestFocus() + { + // We first set the selection models' lead selection to this cell. + table.getColumnModel().getSelectionModel() + .setLeadSelectionIndex(column); + table.getSelectionModel().setLeadSelectionIndex(row); + // Now we request that the table receives focus. + table.requestFocus(); + } + + /** + * Adds a focus listener to this cell. The focus listener is really + * added to the table, so there is no way to find out when an individual + * cell changes the focus. + * + * @param listener the focus listener to add + */ + public void addFocusListener(FocusListener listener) + { + table.addFocusListener(listener); + } + + /** + * Removes a focus listener from the cell. The focus listener is really + * removed from the table. + * + * @param listener the listener to remove + */ + public void removeFocusListener(FocusListener listener) + { + table.removeFocusListener(listener); + } + + } + + protected class AccessibleJTableModelChange + implements AccessibleTableModelChange + { + protected int type; + protected int firstRow; + protected int lastRow; + protected int firstColumn; + protected int lastColumn; + + protected AccessibleJTableModelChange(int type, int firstRow, + int lastRow, int firstColumn, + int lastColumn) + { + this.type = type; + this.firstRow = firstRow; + this.lastRow = lastRow; + this.firstColumn = firstColumn; + this.lastColumn = lastColumn; + } + + public int getType() + { + return type; + } + + public int getFirstRow() + { + return firstRow; + } + + public int getLastRow() + { + return lastRow; + } + + public int getFirstColumn() + { + return firstColumn; + } + + public int getLastColumn() + { + 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 + { + + JTableHeader header; + + int columnIndex; + + /** + * + * @param h the table header. + * @param comp + * @param r + * @param c the column index. + */ + private AccessibleJTableHeaderCell(JTableHeader h, Component comp, int r, + int c) + { + header = h; + columnIndex = c; + } + + /** + * Returns the header renderer. + * + * @return The header renderer. + */ + Component getColumnHeaderRenderer() + { + TableColumn tc = header.getColumnModel().getColumn(columnIndex); + TableCellRenderer r = tc.getHeaderRenderer(); + if (r == null) + r = header.getDefaultRenderer(); + return r.getTableCellRendererComponent(header.getTable(), + tc.getHeaderValue(), false, false, -1, columnIndex); + } + + /** + * Returns the accessible role for the table header cell. + * + * @return The accessible role. + */ + public AccessibleRole getAccessibleRole() + { + Component renderer = getColumnHeaderRenderer(); + if (renderer instanceof Accessible) + { + Accessible ac = (Accessible) renderer; + return ac.getAccessibleContext().getAccessibleRole(); + } + 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; + } + + /** + * Returns the accessible context. + * + * @return this. + */ + public AccessibleContext getAccessibleContext() + { + return this; + } + + 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 AccessibleJTable. + * + * @since JDK1.5 + */ + protected AccessibleJTable() + { + getModel().addTableModelListener(this); + getSelectionModel().addListSelectionListener(this); + getColumnModel().addColumnModelListener(this); + lastSelectedRow = getSelectedRow(); + lastSelectedColumn = getSelectedColumn(); + TableCellEditor editor = getCellEditor(); + if (editor != null) + editor.addCellEditorListener(this); + } + + /** + * Returns the accessible role for the JTable component. + * + * @return {@link AccessibleRole#TABLE}. + */ + public AccessibleRole getAccessibleRole() + { + return AccessibleRole.TABLE; + } + + /** + * Returns the accessible table. + * + * @return this. + */ + public AccessibleTable getAccessibleTable() + { + return this; + } + + /** + * Returns the number of selected items in this table. + */ + public int getAccessibleSelectionCount() + { + return getSelectedColumnCount(); + } + + /** + * Returns the selected accessible object with the specified index + * i. 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 + * i + */ + public Accessible getAccessibleSelection(int i) + { + 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 true if the accessible child with the index + * i is selected, false otherwise. + * + * @param i the index of the accessible to check + * + * @return true if the accessible child with the index + * i is selected, false otherwise + */ + public boolean isAccessibleChildSelected(int i) + { + int r = getAccessibleRowAtIndex(i); + int c = getAccessibleColumnAtIndex(i); + return isCellSelected(r, c); + } + + /** + * Adds the accessible child with the specified index i to the + * selection. + * + * @param i the index of the accessible child to add to the selection + */ + public void addAccessibleSelection(int i) + { + int r = getAccessibleRowAtIndex(i); + int c = getAccessibleColumnAtIndex(i); + changeSelection(r, c, true, false); + } + + /** + * Removes the accessible child with the specified index i + * from the current selection. This will only work on tables that have + * cell selection enabled (rowSelectionAllowed == false && + * columnSelectionAllowed == false). + * + * @param i the index of the accessible to be removed from the selection + */ + public void removeAccessibleSelection(int i) + { + 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() + { + 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() + { + 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) + { + 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; + } + } + + /** + * Receives notification when the table model changes. Depending on the + * type of change, this method calls {@link #tableRowsInserted} or + * {@link #tableRowsDeleted}. + * + * @param event the table model event + */ + public void tableChanged(TableModelEvent event) + { + switch (event.getType()) + { + case TableModelEvent.INSERT: + tableRowsInserted(event); + break; + case TableModelEvent.DELETE: + tableRowsDeleted(event); + break; + } + } + + /** + * Receives notification when one or more rows have been inserted into the + * table and fires appropriate property change events. + * + * @param event the table model event + */ + public void tableRowsInserted(TableModelEvent event) + { + handleRowChange(event); + } + + /** + * Receives notification when one or more rows have been deleted from the + * table. + * + * @param event the table model event + */ + public void tableRowsDeleted(TableModelEvent event) + { + 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) + { + firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, + null, null); + handleColumnChange(AccessibleTableModelChange.INSERT, + event.getFromIndex(), event.getToIndex()); + } + + public void columnRemoved(TableColumnModelEvent event) + { + firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, + null, null); + handleColumnChange(AccessibleTableModelChange.DELETE, + event.getFromIndex(), event.getToIndex()); + } + + public void columnMoved(TableColumnModelEvent event) + { + firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, + null, null); + handleColumnChange(AccessibleTableModelChange.DELETE, + event.getFromIndex(), event.getFromIndex()); + handleColumnChange(AccessibleTableModelChange.INSERT, + event.getFromIndex(), event.getToIndex()); + } + + /** + * 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) + { + 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) + { + // AFAICS, nothing is done here. + } + + public void editingCanceled(ChangeEvent event) + { + // AFAICS, nothing is done here. + } + + public void editingStopped(ChangeEvent event) + { + firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, + null, null); + } + + /** + * Receives notification when any of the JTable's properties changes. This + * is used to replace the listeners on the table's model, selection model, + * column model and cell editor. + * + * @param e the property change event + */ + public void propertyChange(PropertyChangeEvent e) + { + String propName = e.getPropertyName(); + if (propName.equals("tableModel")) + { + TableModel oldModel = (TableModel) e.getOldValue(); + oldModel.removeTableModelListener(this); + TableModel newModel = (TableModel) e.getNewValue(); + newModel.addTableModelListener(this); + } + else if (propName.equals("columnModel")) + { + TableColumnModel oldModel = (TableColumnModel) e.getOldValue(); + oldModel.removeColumnModelListener(this); + TableColumnModel newModel = (TableColumnModel) e.getNewValue(); + newModel.addColumnModelListener(this); + } + else if (propName.equals("selectionModel")) + { + ListSelectionModel oldModel = (ListSelectionModel) e.getOldValue(); + oldModel.removeListSelectionListener(this); + ListSelectionModel newModel = (ListSelectionModel) e.getNewValue(); + newModel.addListSelectionListener(this); + } + else if (propName.equals("cellEditor")) + { + CellEditor oldEd = (CellEditor) e.getOldValue(); + oldEd.removeCellEditorListener(this); + CellEditor newEd = (CellEditor) e.getNewValue(); + newEd.addCellEditorListener(this); + } + } + + /** + * 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) + { + 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) + { + 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) + { + return getAccessibleIndexAt(r, c); + } + + /** + * Returns the caption of the table. + * + * @return the caption of the table + * + * @see #setAccessibleCaption(Accessible) + */ + public Accessible getAccessibleCaption() + { + return caption; + } + + /** + * Sets the caption for the table. + * + * @param c the caption to set + */ + public void setAccessibleCaption(Accessible c) + { + caption = c; + } + + /** + * Returns the summary for the table. + * + * @return the summary for the table + */ + public Accessible getAccessibleSummary() + { + return summary; + } + + /** + * Sets the summary for the table. + * + * @param s the summary to set + */ + public void setAccessibleSummary(Accessible s) + { + summary = s; + } + + /** + * Returns the number of rows in the table. + * + * @return the number of rows in the table + */ + public int getAccessibleRowCount() + { + return getRowCount(); + } + + /** + * Returns the number of columns in the table. + * + * @return the number of columns in the table + */ + public int getAccessibleColumnCount() + { + return getColumnCount(); + } + + /** + * Returns the accessible child at the given index. + * + * @param index the child index. + * + * @return The accessible child. + */ + public Accessible getAccessibleChild(int index) + { + int r = getAccessibleRow(index); + int c = getAccessibleColumn(index); + return getAccessibleAt(r, c); + } + + /** + * 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) + { + TableCellRenderer cellRenderer = getCellRenderer(r, c); + Component renderer = cellRenderer.getTableCellRendererComponent( + JTable.this, getValueAt(r, c), isCellSelected(r, c), false, r, c); + if (renderer instanceof Accessible) + return (Accessible) renderer; + return null; + } + + /** + * Returns the number of rows that the specified cell occupies. The + * standard table cells only occupy one row, so we return 1 + * 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) + { + return 1; + } + + /** + * Returns the number of columns that the specified cell occupies. The + * standard table cells only occupy one column, so we return 1 + * 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) + { + return 1; + } + + /** + * Returns the accessible row header. + * + * @return the accessible row header + */ + public AccessibleTable getAccessibleRowHeader() + { + // 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) + { + // In the RI this seems to be a no-op. + } + + /** + * Returns the column header. + * + * @return the column header, or null if there is no column + * header + */ + public AccessibleTable getAccessibleColumnHeader() + { + 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) + { + // 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 null 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 null if no description has been set + */ + public Accessible getAccessibleRowDescription(int r) + { + 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) + { + if (rowDescriptions == null) + rowDescriptions = new Accessible[getAccessibleRowCount()]; + rowDescriptions[r] = description; + } + + /** + * Returns the accessible description for the column with the specified + * index, or null 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 null if no description has been set + */ + public Accessible getAccessibleColumnDescription(int c) + { + 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) + { + if (columnDescriptions == null) + columnDescriptions = new Accessible[getAccessibleRowCount()]; + columnDescriptions[c] = description; + } + + /** + * Returns true if the accessible child at the specified + * row and column is selected, false otherwise. + * + * @param r the row number of the child + * @param c the column number of the child + * + * @return true if the accessible child at the specified + * row and column is selected, false otherwise + */ + public boolean isAccessibleSelected(int r, int c) + { + return isCellSelected(r, c); + } + + /** + * Returns true if the row with the specified index is + * selected, false otherwise. + * + * @param r the row number + * + * @return true if the row with the specified index is + * selected, false otherwise + */ + public boolean isAccessibleRowSelected(int r) + { + return isRowSelected(r); + } + + /** + * Returns true if the column with the specified index is + * selected, false otherwise. + * + * @param c the column number + * + * @return true if the column with the specified index is + * selected, false otherwise + */ + public boolean isAccessibleColumnSelected(int c) + { + return isColumnSelected(c); + } + + /** + * Returns the indices of all selected rows. + * + * @return the indices of all selected rows + */ + public int[] getSelectedAccessibleRows() + { + return getSelectedRows(); + } + + /** + * Returns the indices of all selected columns. + * + * @return the indices of all selected columns + */ + public int[] getSelectedAccessibleColumns() + { + return getSelectedColumns(); + } + + /** + * Returns the accessible row at the specified index. + * + * @param index the index for which to query the row + * + * @return the row number at the specified table index + */ + public int getAccessibleRowAtIndex(int index) + { + // TODO: Back this up by a Mauve test and update API docs accordingly. + return index / getColumnCount(); + } + + /** + * Returns the accessible column at the specified index. + * + * @param index the index for which to query the column + * + * @return the column number at the specified table index + */ + public int getAccessibleColumnAtIndex(int index) + { + // TODO: Back this up by a Mauve test and update API docs accordingly. + return index % getColumnCount(); + } + + /** + * Returns the accessible child index at the specified column and row. + * + * @param row the row + * @param column the column + * + * @return the index of the accessible child at the specified row and + * column + */ + public int getAccessibleIndexAt(int row, int column) + { + // TODO: Back this up by a Mauve test and update API docs accordingly. + return row * getColumnCount() + column; + } + } + /** + * Handles property changes from the TableColumns of this + * JTable. + * + * More specifically, this triggers a {@link #revalidate()} call if the + * preferredWidth of one of the observed columns changes. + */ + class TableColumnPropertyChangeHandler implements PropertyChangeListener + { + /** + * Receives notification that a property of the observed TableColumns has + * changed. + * + * @param ev the property change event + */ + public void propertyChange(PropertyChangeEvent ev) + { + if (ev.getPropertyName().equals("preferredWidth")) + { + JTableHeader header = getTableHeader(); + if (header != null) + // Do nothing if the table is in the resizing mode. + if (header.getResizingColumn() == null) + { + TableColumn col = (TableColumn) ev.getSource(); + header.setResizingColumn(col); + doLayout(); + header.setResizingColumn(null); + } + } + } + } + + /** + * A cell renderer for boolean values. + */ + private class BooleanCellRenderer + extends DefaultTableCellRenderer + { + /** + * The CheckBox that is used for rendering. + */ + 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. + */ + JCheckBox getCheckBox() + { + return checkBox; + } + + /** + * Returns the component that is used for rendering the value. + * + * @param table the JTable + * @param value the value of the object + * @param isSelected is the cell selected? + * @param hasFocus has the cell the focus? + * @param row the row to render + * @param column the cell to render + * @return this component (the default table cell renderer) + */ + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, + boolean hasFocus, int row, + int column) + { + if (isSelected) + { + checkBox.setBackground(table.getSelectionBackground()); + checkBox.setForeground(table.getSelectionForeground()); + } + else + { + checkBox.setBackground(table.getBackground()); + checkBox.setForeground(table.getForeground()); + } + + if (hasFocus) + { + checkBox.setBorder( + UIManager.getBorder("Table.focusCellHighlightBorder")); + if (table.isCellEditable(row, column)) + { + checkBox.setBackground( + UIManager.getColor("Table.focusCellBackground")); + checkBox.setForeground( + UIManager.getColor("Table.focusCellForeground")); + } + } + else + checkBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); + + // Null is rendered as false. + if (value == null) + checkBox.setSelected(false); + else + { + Boolean boolValue = (Boolean) value; + checkBox.setSelected(boolValue.booleanValue()); + } + return checkBox; + } + } + + /** + * A cell renderer for Date values. + */ + private class DateCellRenderer + extends DefaultTableCellRenderer + { + /** + * Returns the component that is used for rendering the value. + * + * @param table the JTable + * @param value the value of the object + * @param isSelected is the cell selected? + * @param hasFocus has the cell the focus? + * @param row the row to render + * @param column the cell to render + * + * @return this component (the default table cell renderer) + */ + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, + boolean hasFocus, int row, + int column) + { + super.getTableCellRendererComponent(table, value, isSelected, hasFocus, + row, column); + if (value instanceof Date) + { + Date dateValue = (Date) value; + DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT); + setText(df.format(dateValue)); + } + return this; + } + } + + /** + * A cell renderer for Double values. + */ + private class DoubleCellRenderer + extends DefaultTableCellRenderer + { + /** + * Creates a new instance of NumberCellRenderer. + */ + public DoubleCellRenderer() + { + setHorizontalAlignment(JLabel.RIGHT); + } + + /** + * Returns the component that is used for rendering the value. + * + * @param table the JTable + * @param value the value of the object + * @param isSelected is the cell selected? + * @param hasFocus has the cell the focus? + * @param row the row to render + * @param column the cell to render + * + * @return this component (the default table cell renderer) + */ + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, + boolean hasFocus, int row, + int column) + { + super.getTableCellRendererComponent(table, value, isSelected, hasFocus, + row, column); + if (value instanceof Double) + { + Double doubleValue = (Double) value; + NumberFormat nf = NumberFormat.getInstance(); + setText(nf.format(doubleValue.doubleValue())); + } + return this; + } + } + + /** + * A cell renderer for Float values. + */ + private class FloatCellRenderer + extends DefaultTableCellRenderer + { + /** + * Creates a new instance of NumberCellRenderer. + */ + public FloatCellRenderer() + { + setHorizontalAlignment(JLabel.RIGHT); + } + + /** + * Returns the component that is used for rendering the value. + * + * @param table the JTable + * @param value the value of the object + * @param isSelected is the cell selected? + * @param hasFocus has the cell the focus? + * @param row the row to render + * @param column the cell to render + * + * @return this component (the default table cell renderer) + */ + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, + boolean hasFocus, int row, + int column) + { + super.getTableCellRendererComponent(table, value, isSelected, hasFocus, + row, column); + if (value instanceof Float) + { + Float floatValue = (Float) value; + NumberFormat nf = NumberFormat.getInstance(); + setText(nf.format(floatValue.floatValue())); + } + return this; + } + } + + /** + * A cell renderer for Number values. + */ + private class NumberCellRenderer + extends DefaultTableCellRenderer + { + /** + * Creates a new instance of NumberCellRenderer. + */ + public NumberCellRenderer() + { + setHorizontalAlignment(JLabel.RIGHT); + } + } + + /** + * A cell renderer for Icon values. + */ + private class IconCellRenderer + extends DefaultTableCellRenderer + { + IconCellRenderer() + { + setHorizontalAlignment(SwingConstants.CENTER); + } + + + /** + * Returns the component that is used for rendering the value. + * + * @param table the JTable + * @param value the value of the object + * @param isSelected is the cell selected? + * @param hasFocus has the cell the focus? + * @param row the row to render + * @param column the cell to render + * + * @return this component (the default table cell renderer) + */ + public Component getTableCellRendererComponent(JTable table, Object value, + boolean isSelected, + boolean hasFocus, int row, + int column) + { + super.getTableCellRendererComponent(table, value, isSelected, hasFocus, + row, column); + if (value instanceof Icon) + { + Icon iconValue = (Icon) value; + setIcon(iconValue); + } + else + { + setIcon(null); + } + setText(""); + return this; + } + } + + /** + * The JTable text component (used in editing) always has the table + * as its parent. The scrollRectToVisible must be adjusted taking the + * relative component position. + * + * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) + */ + private class TableTextField extends JTextField + { + /** + * Create the text field without the border. + */ + TableTextField() + { + setBorder(BorderFactory.createLineBorder(getGridColor(), 2)); + } + } + + + private static final long serialVersionUID = 3876025080382781659L; + + /** + * This table, for referring identically name methods from inner classes. + */ + final JTable this_table = this; + + + /** + * When resizing columns, do not automatically change any columns. In this + * case the table should be enclosed in a {@link JScrollPane} in order to + * accomodate cases in which the table size exceeds its visible area. + */ + public static final int AUTO_RESIZE_OFF = 0; + + /** + * When resizing column i, automatically change only the + * single column i+1 to provide or absorb excess space + * requirements. + */ + public static final int AUTO_RESIZE_NEXT_COLUMN = 1; + + /** + * When resizing column i in a table of n + * columns, automatically change all columns in the range [i+1, + * n), uniformly, to provide or absorb excess space requirements. + */ + public static final int AUTO_RESIZE_SUBSEQUENT_COLUMNS = 2; + + /** + * When resizing column i in a table of n + * columns, automatically change all columns in the range [0, + * n) (with the exception of column i) uniformly, to provide or + * absorb excess space requirements. + */ + public static final int AUTO_RESIZE_ALL_COLUMNS = 4; + + /** + * When resizing column i in a table of n + * columns, automatically change column n-1 (the last column + * in the table) to provide or absorb excess space requirements. + */ + public static final int AUTO_RESIZE_LAST_COLUMN = 3; + + /** + * A table mapping {@link java.lang.Class} objects to + * {@link TableCellEditor} objects. This table is consulted by the + * FIXME + */ + 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 = new Hashtable(); + + /** + * The column that is edited, -1 if the table is not edited currently. + */ + protected int editingColumn; + + /** + * The row that is edited, -1 if the table is not edited currently. + */ + protected int editingRow; + + /** + * The component that is used for editing. + * null if the table is not editing currently. + * + */ + protected transient Component editorComp; + + + /** + * Whether or not the table should automatically compute a matching + * {@link TableColumnModel} and assign it to the {@link #columnModel} + * property when the {@link #dataModel} property is changed. + * + * @see #setModel(TableModel) + * @see #createDefaultColumnsFromModel() + * @see #setColumnModel(TableColumnModel) + * @see #setAutoCreateColumnsFromModel(boolean) + * @see #getAutoCreateColumnsFromModel() + */ + protected boolean autoCreateColumnsFromModel; + + /** + * A numeric code specifying the resizing behavior of the table. Must be + * one of {@link #AUTO_RESIZE_ALL_COLUMNS} (the default), {@link + * #AUTO_RESIZE_LAST_COLUMN}, {@link #AUTO_RESIZE_NEXT_COLUMN}, {@link + * #AUTO_RESIZE_SUBSEQUENT_COLUMNS}, or {@link #AUTO_RESIZE_OFF}. + * + * @see #doLayout() + * @see #setAutoResizeMode(int) + * @see #getAutoResizeMode() + */ + protected int autoResizeMode; + + /** + * The height in pixels of any row of the table. All rows in a table are + * of uniform height. This differs from column width, which varies on a + * per-column basis, and is stored in the individual columns of the + * {@link #columnModel}. + * + * @see #getRowHeight() + * @see #setRowHeight(int) + * @see TableColumn#getWidth() + * @see TableColumn#setWidth(int) + */ + protected int rowHeight; + + /** + * The height in pixels of the gap left between any two rows of the table. + * + * @see #setRowMargin(int) + * @see #getRowHeight() + * @see #getIntercellSpacing() + * @see #setIntercellSpacing(Dimension) + * @see TableColumnModel#getColumnMargin() + * @see TableColumnModel#setColumnMargin(int) + */ + protected int rowMargin; + + /** + * Whether or not the table should allow row selection. If the table + * allows both row and column selection, it is said to allow + * "cell selection". Previous versions of the JDK supported cell + * selection as an independent concept, but it is now represented solely + * in terms of simultaneous row and column selection. + * + * @see TableColumnModel#getColumnSelectionAllowed() + * @see #setRowSelectionAllowed(boolean) + * @see #getRowSelectionAllowed() + * @see #getCellSelectionEnabled() + * @see #setCellSelectionEnabled(boolean) + */ + protected boolean rowSelectionAllowed; + + /** + * Obsolete. Use {@link #rowSelectionAllowed}, {@link + * #getColumnSelectionAllowed}, or the combined methods {@link + * #getCellSelectionEnabled} and {@link #setCellSelectionEnabled(boolean)}. + */ + protected boolean cellSelectionEnabled; + + /** + * The model for data stored in the table. Confusingly, the published API + * requires that this field be called dataModel, despite its + * property name. The table listens to its model as a {@link + * TableModelListener}. + * + * @see #tableChanged(TableModelEvent) + * @see TableModel#addTableModelListener(TableModelListener) + */ + protected TableModel dataModel; + + /** + *

A model of various aspects of the columns of the table, not + * including the data stored in them. The {@link TableColumnModel} + * is principally concerned with holding a set of {@link TableColumn} + * objects, each of which describes the display parameters of a column + * and the numeric index of the column from the data model which the + * column is presenting.

+ * + *

The TableColumnModel also contains a {@link ListSelectionModel} which + * indicates which columns are currently selected. This selection model + * works in combination with the {@link #selectionModel} of the table + * itself to specify a table selection: a combination of row and + * column selections.

+ * + *

Most application programmers do not need to work with this property + * at all: setting {@link #autoCreateColumnsFromModel} will construct the + * columnModel automatically, and the table acts as a facade for most of + * the interesting properties of the columnModel anyways.

+ * + * @see #setColumnModel(TableColumnModel) + * @see #getColumnModel() + */ + protected TableColumnModel columnModel; + + /** + * A model of the rows of this table which are currently selected. This + * model is used in combination with the column selection model held as a + * member of the {@link #columnModel} property, to represent the rows and + * columns (or both: cells) of the table which are currently selected. + * + * @see #rowSelectionAllowed + * @see #setSelectionModel(ListSelectionModel) + * @see #getSelectionModel() + * @see TableColumnModel#getSelectionModel() + * @see ListSelectionModel#addListSelectionListener(ListSelectionListener) + */ + protected ListSelectionModel selectionModel; + + /** + * The current cell editor. + */ + protected TableCellEditor cellEditor; + + /** + * Whether or not drag-and-drop is enabled on this table. + * + * @see #setDragEnabled(boolean) + * @see #getDragEnabled() + */ + private boolean dragEnabled; + + /** + * The color to paint the grid lines of the table, when either {@link + * #showHorizontalLines} or {@link #showVerticalLines} is set. + * + * @see #setGridColor(Color) + * @see #getGridColor() + */ + protected Color gridColor; + + /** + * The size this table would prefer its viewport assume, if it is + * contained in a {@link JScrollPane}. + * + * @see #setPreferredScrollableViewportSize(Dimension) + * @see #getPreferredScrollableViewportSize() + */ + protected Dimension preferredViewportSize; + + /** + * The color to paint the background of selected cells. Fires a property + * change event with name {@link #SELECTION_BACKGROUND_CHANGED_PROPERTY} + * when its value changes. + * + * @see #setSelectionBackground(Color) + * @see #getSelectionBackground() + */ + protected Color selectionBackground; + + /** + * The name carried in property change events when the {@link + * #selectionBackground} property changes. + */ + private static final String SELECTION_BACKGROUND_CHANGED_PROPERTY = "selectionBackground"; + + /** + * The color to paint the foreground of selected cells. Fires a property + * change event with name {@link #SELECTION_FOREGROUND_CHANGED_PROPERTY} + * when its value changes. + * + * @see #setSelectionForeground(Color) + * @see #getSelectionForeground() + */ + protected Color selectionForeground; + + /** + * The name carried in property change events when the + * {@link #selectionForeground} property changes. + */ + private static final String SELECTION_FOREGROUND_CHANGED_PROPERTY = "selectionForeground"; + + /** + * The showHorizontalLines property. + */ + protected boolean showHorizontalLines; + + /** + * The showVerticalLines property. + */ + protected boolean showVerticalLines; + + /** + * The tableHeader property. + */ + protected JTableHeader tableHeader; + + /** + * The property handler for this table's columns. + */ + TableColumnPropertyChangeHandler tableColumnPropertyChangeHandler = + new TableColumnPropertyChangeHandler(); + + /** + * Whether cell editors should receive keyboard focus when the table is + * activated. + */ + private boolean surrendersFocusOnKeystroke = false; + + /** + * A Rectangle object to be reused in {@link #getCellRect}. + */ + private Rectangle rectCache = new Rectangle(); + + /** + * Indicates if the rowHeight property has been set by a client program or by + * the UI. + * + * @see #setUIProperty(String, Object) + * @see LookAndFeel#installProperty(JComponent, String, Object) + */ + 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 JTable instance. + */ + public JTable () + { + this(null, null, null); + } + + /** + * Creates a new JTable instance with the given number + * of rows and columns. + * + * @param numRows an int value + * @param numColumns an int value + */ + public JTable (int numRows, int numColumns) + { + this(new DefaultTableModel(numRows, numColumns)); + } + + /** + * Creates a new JTable instance, storing the given data + * array and heaving the given column names. To see the column names, + * you must place the JTable into the {@link JScrollPane}. + * + * @param data an Object[][] the table data + * @param columnNames an Object[] the column headers + */ + public JTable(Object[][] data, Object[] columnNames) + { + this(new DefaultTableModel(data, columnNames)); + } + + /** + * Creates a new JTable instance, using the given data model + * object that provides information about the table content. The table model + * object is asked for the table size, other features and also receives + * notifications in the case when the table has been edited by the user. + * + * @param model + * the table model. + */ + public JTable (TableModel model) + { + this(model, null, null); + } + + /** + * Creates a new JTable instance, using the given model object + * that provides information about the table content. The table data model + * object is asked for the table size, other features and also receives + * notifications in the case when the table has been edited by the user. The + * table column model provides more detailed control on the table column + * related features. + * + * @param dm + * the table data mode + * @param cm + * the table column model + */ + public JTable (TableModel dm, TableColumnModel cm) + { + this(dm, cm, null); + } + + /** + * Creates a new JTable instance, providing data model, + * column model and list selection model. The list selection model + * manages the selections. + * + * @param dm data model (manages table data) + * @param cm column model (manages table columns) + * @param sm list selection model (manages table selections) + */ + public JTable (TableModel dm, TableColumnModel cm, ListSelectionModel sm) + { + boolean autoCreate = false; + TableColumnModel columnModel; + if (cm != null) + columnModel = cm; + else + { + columnModel = createDefaultColumnModel(); + autoCreate = true; + } + + // Initialise the intercelar spacing before setting the column model to + // avoid firing unnecessary events. + // The initial incellar spacing is new Dimenstion(1,1). + rowMargin = 1; + columnModel.setColumnMargin(1); + setColumnModel(columnModel); + + setSelectionModel(sm == null ? createDefaultSelectionModel() : sm); + setModel(dm == null ? createDefaultDataModel() : dm); + setAutoCreateColumnsFromModel(autoCreate); + initializeLocalVars(); + + // The following four lines properly set the lead selection indices. + // After this, the UI will handle the lead selection indices. + // FIXME: this should probably not be necessary, if the UI is installed + // before the TableModel is set then the UI will handle things on its + // own, but certain variables need to be set before the UI can be installed + // so we must get the correct order for all the method calls in this + // constructor. + // These four lines are not needed. A Mauve test that shows this is + // gnu.testlet.javax.swing.JTable.constructors(linesNotNeeded). + // selectionModel.setAnchorSelectionIndex(-1); + // selectionModel.setLeadSelectionIndex(-1); + // columnModel.getSelectionModel().setAnchorSelectionIndex(-1); + // columnModel.getSelectionModel().setLeadSelectionIndex(-1); + updateUI(); + } + + /** + * Creates a new JTable instance that uses data and column + * names, stored in {@link Vector}s. + * + * @param data the table data + * @param columnNames the table column names. + */ + public JTable(Vector data, Vector columnNames) + { + this(new DefaultTableModel(data, columnNames)); + } + + /** + * Initialize local variables to default values. + */ + protected void initializeLocalVars() + { + setTableHeader(createDefaultTableHeader()); + if (autoCreateColumnsFromModel) + createDefaultColumnsFromModel(); + this.columnModel.addColumnModelListener(this); + + this.autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS; + setRowHeight(16); + this.rowMargin = 1; + this.rowSelectionAllowed = true; + + // this.accessibleContext = new AccessibleJTable(); + this.cellEditor = null; + + // COMPAT: Both Sun and IBM have drag enabled + this.dragEnabled = false; + this.preferredViewportSize = new Dimension(450,400); + this.showHorizontalLines = true; + this.showVerticalLines = true; + this.editingColumn = -1; + this.editingRow = -1; + } + + /** + * Add the new table column. The table column class allows to specify column + * features more precisely, setting the preferred width, column data type + * (column class) and table headers. + * + * There is no need the add columns to the table if the default column + * handling is sufficient. + * + * @param column + * the new column to add. + */ + public void addColumn(TableColumn column) + { + if (column.getHeaderValue() == null) + { + String name = dataModel.getColumnName(column.getModelIndex()); + column.setHeaderValue(name); + } + + columnModel.addColumn(column); + column.addPropertyChangeListener(tableColumnPropertyChangeHandler); + } + + /** + * Create the default editors for this table. The default method creates + * the editor for Booleans. + * + * Other fields are edited as strings at the moment. + */ + protected void createDefaultEditors() + { + JCheckBox box = new BooleanCellRenderer().getCheckBox(); + box.setBorder(BorderFactory.createLineBorder(getGridColor(), 2)); + box.setBorderPainted(true); + booleanInvertingEditor = new DefaultCellEditor(box); + setDefaultEditor(Boolean.class, booleanInvertingEditor); + } + + /** + * Create the default renderers for this table. The default method creates + * renderers for Boolean, Number, Double, Date, Icon and ImageIcon. + * + */ + protected void createDefaultRenderers() + { + setDefaultRenderer(Boolean.class, new BooleanCellRenderer()); + setDefaultRenderer(Number.class, new NumberCellRenderer()); + setDefaultRenderer(Double.class, new DoubleCellRenderer()); + setDefaultRenderer(Double.class, new FloatCellRenderer()); + setDefaultRenderer(Date.class, new DateCellRenderer()); + setDefaultRenderer(Icon.class, new IconCellRenderer()); + setDefaultRenderer(ImageIcon.class, new IconCellRenderer()); + } + + /** + * @deprecated 1.0.2, replaced by new JScrollPane(JTable) + */ + public static JScrollPane createScrollPaneForTable(JTable table) + { + return new JScrollPane(table); + } + + /** + * Create the default table column model that is used if the user-defined + * column model is not provided. The default method creates + * {@link DefaultTableColumnModel}. + * + * @return the created table column model. + */ + protected TableColumnModel createDefaultColumnModel() + { + return new DefaultTableColumnModel(); + } + + /** + * Create the default table data model that is used if the user-defined + * data model is not provided. The default method creates + * {@link DefaultTableModel}. + * + * @return the created table data model. + */ + protected TableModel createDefaultDataModel() + { + return new DefaultTableModel(); + } + + /** + * Create the default table selection model that is used if the user-defined + * selection model is not provided. The default method creates + * {@link DefaultListSelectionModel}. + * + * @return the created table data model. + */ + protected ListSelectionModel createDefaultSelectionModel() + { + return new DefaultListSelectionModel(); + } + + /** + * Create the default table header, if the user - defined table header is not + * provided. + * + * @return the default table header. + */ + protected JTableHeader createDefaultTableHeader() + { + return new JTableHeader(columnModel); + } + + /** + * Invoked when the column is added. Revalidates and repains the table. + */ + public void columnAdded (TableColumnModelEvent event) + { + revalidate(); + repaint(); + } + + /** + * Invoked when the column margin is changed. + * Revalidates and repains the table. + */ + public void columnMarginChanged (ChangeEvent event) + { + revalidate(); + repaint(); + } + + /** + * Invoked when the column is moved. Revalidates and repains the table. + */ + public void columnMoved (TableColumnModelEvent event) + { + if (isEditing()) + editingCanceled(null); + revalidate(); + repaint(); + } + + /** + * Invoked when the column is removed. Revalidates and repains the table. + */ + public void columnRemoved (TableColumnModelEvent event) + { + revalidate(); + repaint(); + } + + /** + * Invoked when the the column selection changes, repaints the changed + * columns. It is not recommended to override this method, register the + * listener instead. + */ + public void columnSelectionChanged (ListSelectionEvent event) + { + // 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. + int idx0 = Math.max(0, Math.min(getColumnCount() - 1, + event.getFirstIndex())); + int idxn = Math.max(0, Math.min(getColumnCount() - 1, + event.getLastIndex())); + + 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); + } + } + + /** + * Invoked when the editing is cancelled. + */ + public void editingCanceled (ChangeEvent event) + { + if (editorComp!=null) + { + remove(editorComp); + repaint(editorComp.getBounds()); + editorComp = null; + } + } + + /** + * Finish the current editing session and update the table with the + * new value by calling {@link #setValueAt}. + * + * @param event the change event + */ + public void editingStopped (ChangeEvent event) + { + if (editorComp!=null) + { + remove(editorComp); + setValueAt(cellEditor.getCellEditorValue(), editingRow, editingColumn); + repaint(editorComp.getBounds()); + editorComp = null; + } + requestFocusInWindow(); + } + + /** + * Invoked when the table changes. + * null means everything changed. + */ + public void tableChanged (TableModelEvent event) + { + // update the column model from the table model if the structure has + // changed and the flag autoCreateColumnsFromModel is set + if (event == null || (event.getFirstRow() == TableModelEvent.HEADER_ROW)) + handleCompleteChange(event); + else if (event.getType() == TableModelEvent.INSERT) + handleInsert(event); + else if (event.getType() == TableModelEvent.DELETE) + handleDelete(event); + else + handleUpdate(event); + } + + /** + * Handles a request for complete relayout. This is the case when + * event.getFirstRow() == TableModelEvent.HEADER_ROW. + * + * @param ev the table model event + */ + private void handleCompleteChange(TableModelEvent ev) + { + clearSelection(); + checkSelection(); + rowHeights = null; + if (getAutoCreateColumnsFromModel()) + createDefaultColumnsFromModel(); + else + resizeAndRepaint(); + } + + /** + * Handles table model insertions. + * + * @param ev the table model event + */ + private void handleInsert(TableModelEvent ev) + { + // Sync selection model with data model. + int first = ev.getFirstRow(); + if (first < 0) + first = 0; + int last = ev.getLastRow(); + if (last < 0) + last = getRowCount() - 1; + selectionModel.insertIndexInterval(first, last - first + 1, true); + checkSelection(); + + // For variable height rows we must update the SizeSequence thing. + if (rowHeights != null) + { + rowHeights.insertEntries(first, last - first + 1, rowHeight); + // TODO: We repaint the whole thing when the rows have variable + // heights. We might want to handle this better though. + repaint(); + } + else + { + // Repaint the dirty region and revalidate. + int rowHeight = getRowHeight(); + Rectangle dirty = new Rectangle(0, first * rowHeight, + getColumnModel().getTotalColumnWidth(), + (getRowCount() - first) * rowHeight); + repaint(dirty); + } + revalidate(); + } + + /** + * Handles table model deletions. + * + * @param ev the table model event + */ + private void handleDelete(TableModelEvent ev) + { + // Sync selection model with data model. + int first = ev.getFirstRow(); + if (first < 0) + first = 0; + int last = ev.getLastRow(); + if (last < 0) + last = getRowCount() - 1; + + selectionModel.removeIndexInterval(first, last); + + checkSelection(); + + if (dataModel.getRowCount() == 0) + clearSelection(); + + // For variable height rows we must update the SizeSequence thing. + if (rowHeights != null) + { + rowHeights.removeEntries(first, last - first + 1); + // TODO: We repaint the whole thing when the rows have variable + // heights. We might want to handle this better though. + repaint(); + } + else + { + // Repaint the dirty region and revalidate. + int rowHeight = getRowHeight(); + int oldRowCount = getRowCount() + last - first + 1; + Rectangle dirty = new Rectangle(0, first * rowHeight, + getColumnModel().getTotalColumnWidth(), + (oldRowCount - first) * rowHeight); + repaint(dirty); + } + revalidate(); + } + + /** + * Handles table model updates without structural changes. + * + * @param ev the table model event + */ + private void handleUpdate(TableModelEvent ev) + { + if (rowHeights == null) + { + // Some cells have been changed without changing the structure. + // Figure out the dirty rectangle and repaint. + int firstRow = ev.getFirstRow(); + int lastRow = ev.getLastRow(); + int col = ev.getColumn(); + Rectangle dirty; + if (col == TableModelEvent.ALL_COLUMNS) + { + // All columns changed. + dirty = new Rectangle(0, firstRow * getRowHeight(), + getColumnModel().getTotalColumnWidth(), 0); + } + else + { + // Only one cell or column of cells changed. + // We need to convert to view column first. + int column = convertColumnIndexToModel(col); + dirty = getCellRect(firstRow, column, false); + } + + // Now adjust the height of the dirty region. + dirty.height = (lastRow + 1) * getRowHeight(); + // .. and repaint. + repaint(dirty); + } + else + { + // TODO: We repaint the whole thing when the rows have variable + // heights. We might want to handle this better though. + repaint(); + } + } + + /** + * Helper method for adjusting the lead and anchor indices when the + * table structure changed. This sets the lead and anchor to -1 if there's + * no more rows, or set them to 0 when they were at -1 and there are actually + * some rows now. + */ + private void checkSelection() + { + TableModel m = getModel(); + ListSelectionModel sm = selectionModel; + if (m != null) + { + int lead = sm.getLeadSelectionIndex(); + int c = m.getRowCount(); + if (c == 0 && lead != -1) + { + // No rows in the model, reset lead and anchor to -1. + sm.setValueIsAdjusting(true); + sm.setAnchorSelectionIndex(-1); + sm.setLeadSelectionIndex(-1); + sm.setValueIsAdjusting(false); + } + else if (c != 0 && lead == -1) + { + // We have rows, but no lead/anchor. Set them to 0. We + // do a little trick here so that the actual selection is not + // touched. + if (sm.isSelectedIndex(0)) + sm.addSelectionInterval(0, 0); + else + sm.removeSelectionInterval(0, 0); + } + // Nothing to do in the other cases. + } + } + + /** + * Invoked when another table row is selected. It is not recommended + * to override thid method, register the listener instead. + */ + public void valueChanged (ListSelectionEvent event) + { + // If we are in the editing process, end the editing session. + if (isEditing()) + editingStopped(null); + + // 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); + } + + /** + * Returns index of the column that contains specified point + * or -1 if this table doesn't contain this point. + * + * @param point point to identify the column + * @return index of the column that contains specified point or + * -1 if this table doesn't contain this point. + */ + public int columnAtPoint(Point point) + { + int ncols = getColumnCount(); + Dimension gap = getIntercellSpacing(); + TableColumnModel cols = getColumnModel(); + int x = point.x; + + for (int i = 0; i < ncols; ++i) + { + int width = cols.getColumn(i).getWidth() + + (gap == null ? 0 : gap.width); + if (0 <= x && x < width) + return i; + x -= width; + } + return -1; + } + + /** + * Returns index of the row that contains specified point or -1 if this table + * doesn't contain this point. + * + * @param point point to identify the row + * @return index of the row that contains specified point or -1 if this table + * doesn't contain this point. + */ + public int rowAtPoint(Point point) + { + if (point != null) + { + int nrows = getRowCount(); + int r; + int y = point.y; + if (rowHeights == null) + { + int height = getRowHeight(); + r = y / height; + } + else + r = rowHeights.getIndex(y); + + if (r < 0 || r >= nrows) + return -1; + else + return r; + } + else + return -1; + } + + /** + * Calculate the visible rectangle for a particular row and column. The + * row and column are specified in visual terms; the column may not match + * the {@link #dataModel} column. + * + * @param row the visible row to get the cell rectangle of + * + * @param column the visible column to get the cell rectangle of, which may + * differ from the {@link #dataModel} column + * + * @param includeSpacing whether or not to include the cell margins in the + * resulting cell. If false, the result will only contain the + * inner area of the target cell, not including its margins. + * + * @return a rectangle enclosing the specified cell + */ + public Rectangle getCellRect(int row, + int column, + boolean includeSpacing) + { + Rectangle cellRect = new Rectangle(0, 0, 0, 0); + + // 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); + + if (! includeSpacing) + { + // The rounding here is important. + int rMargin = getRowMargin(); + cellRect.y += rMargin / 2; + cellRect.height -= rMargin; + } + } + // else row < 0, y = height = 0 + + // 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 + { + 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() + { + selectionModel.clearSelection(); + getColumnModel().getSelectionModel().clearSelection(); + } + + /** + * Get the value of the selectedRow property by delegation to + * the {@link ListSelectionModel#getMinSelectionIndex} method of the + * {@link #selectionModel} field. + * + * @return The current value of the selectedRow property + */ + public int getSelectedRow () + { + return selectionModel.getMinSelectionIndex(); + } + + /** + * Get the value of the {@link #selectionModel} property. + * + * @return The current value of the property + */ + public ListSelectionModel getSelectionModel() + { + //Neither Sun nor IBM returns null if rowSelection not allowed + return selectionModel; + } + + public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) + { + int block; + if (orientation == SwingConstants.HORIZONTAL) + { + block = visibleRect.width; + } + else + { + int rowHeight = getRowHeight(); + if (rowHeight > 0) + block = Math.max(rowHeight, // Little hack for useful rounding. + (visibleRect.height / rowHeight) * rowHeight); + else + block = visibleRect.height; + } + return block; + } + + /** + * Get the value of the scrollableTracksViewportHeight property. + * + * @return The constant value false + */ + public boolean getScrollableTracksViewportHeight() + { + return false; + } + + /** + * Get the value of the scrollableTracksViewportWidth property. + * + * @return true unless the {@link #autoResizeMode} property is + * AUTO_RESIZE_OFF + */ + public boolean getScrollableTracksViewportWidth() + { + if (autoResizeMode == AUTO_RESIZE_OFF) + return false; + else + return true; + } + + /** + * Return the preferred scrolling amount (in pixels) for the given scrolling + * direction and orientation. This method handles a partially exposed row by + * returning the distance required to completely expose the item. When + * scrolling the top item is completely exposed. + * + * @param visibleRect the currently visible part of the component. + * @param orientation the scrolling orientation + * @param direction the scrolling direction (negative - up, positive -down). + * The values greater than one means that more mouse wheel or similar + * events were generated, and hence it is better to scroll the longer + * distance. + * + * @author Roman Kennke (kennke@aicas.com) + */ + public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, + int direction) + { + int unit; + if (orientation == SwingConstants.HORIZONTAL) + unit = 100; + else + { + unit = getRowHeight(); + // The following adjustment doesn't work for variable height rows. + // It fully exposes partially visible rows in the scrolling direction. + if (rowHeights == null) + { + if (direction > 0) + { + // Scroll down. + // How much pixles are exposed from the last item? + int exposed = (visibleRect.y + visibleRect.height) % unit; + if (exposed > 0 && exposed < unit - 1) + unit = unit - exposed - 1; + } + else + { + // Scroll up. + int exposed = visibleRect.y % unit; + if (exposed > 0 && exposed < unit) + unit = exposed; + } + } + } + return unit; + } + + /** + * Get the cell editor, suitable for editing the given cell. The default + * method requests the editor from the column model. If the column model does + * not provide the editor, the call is forwarded to the + * {@link #getDefaultEditor(Class)} with the parameter, obtained from + * {@link TableModel#getColumnClass(int)}. + * + * @param row the cell row + * @param column the cell column + * @return the editor to edit that cell + */ + public TableCellEditor getCellEditor(int row, int column) + { + TableCellEditor editor = columnModel.getColumn(column).getCellEditor(); + + if (editor == null) + { + int mcolumn = convertColumnIndexToModel(column); + editor = getDefaultEditor(dataModel.getColumnClass(mcolumn)); + } + + return editor; + } + + /** + * Get the default editor for editing values of the given type + * (String, Boolean and so on). + * + * @param columnClass the class of the value that will be edited. + * + * @return the editor, suitable for editing this data type + */ + public TableCellEditor getDefaultEditor(Class columnClass) + { + if (defaultEditorsByColumnClass.containsKey(columnClass)) + return (TableCellEditor) defaultEditorsByColumnClass.get(columnClass); + else + { + JTextField t = new TableTextField(); + TableCellEditor r = new DefaultCellEditor(t); + defaultEditorsByColumnClass.put(columnClass, r); + return r; + } + } + + /** + * Get the cell renderer for rendering the given cell. + * + * @param row the cell row + * @param column the cell column + * @return the cell renderer to render that cell. + */ + public TableCellRenderer getCellRenderer(int row, int column) + { + TableCellRenderer renderer = null; + if (columnModel.getColumnCount() > 0) + renderer = columnModel.getColumn(column).getCellRenderer(); + if (renderer == null) + { + int mcolumn = convertColumnIndexToModel(column); + renderer = getDefaultRenderer(dataModel.getColumnClass(mcolumn)); + } + return renderer; + } + + /** + * Set default renderer for rendering the given data type. + * + * @param columnClass the data type (String, Boolean and so on) that must be + * rendered. + * @param rend the renderer that will rend this data type + */ + public void setDefaultRenderer(Class columnClass, TableCellRenderer rend) + { + defaultRenderersByColumnClass.put(columnClass, rend); + } + + /** + * Get the default renderer for rendering the given data type. + * + * @param columnClass the data that must be rendered + * + * @return the appropriate defauld renderer for rendering that data type. + */ + public TableCellRenderer getDefaultRenderer(Class columnClass) + { + if (defaultRenderersByColumnClass.containsKey(columnClass)) + return (TableCellRenderer) defaultRenderersByColumnClass.get(columnClass); + else + { + TableCellRenderer r = new DefaultTableCellRenderer(); + defaultRenderersByColumnClass.put(columnClass, r); + return r; + } + } + + /** + * Convert the table model index into the table column number. + * The model number need not match the real column position. The columns + * may be rearranged by the user with mouse at any time by dragging the + * column headers. + * + * @param vc the column number (0=first). + * + * @return the table column model index of this column. + * + * @see TableColumn#getModelIndex() + */ + public int convertColumnIndexToModel(int vc) + { + if (vc < 0) + return vc; + else + return columnModel.getColumn(vc).getModelIndex(); + } + + /** + * Convert the table column number to the table column model index. + * The model number need not match the real column position. The columns + * may be rearranged by the user with mouse at any time by dragging the + * column headers. + * + * @param mc the table column index (0=first). + * + * @return the table column number in the model + * + * @see TableColumn#getModelIndex() + */ + public int convertColumnIndexToView(int mc) + { + if (mc < 0) + return mc; + int ncols = getColumnCount(); + for (int vc = 0; vc < ncols; ++vc) + { + if (columnModel.getColumn(vc).getModelIndex() == mc) + return vc; + } + return -1; + } + + /** + * Prepare the renderer for rendering the given cell. + * + * @param renderer the renderer being prepared + * @param row the row of the cell being rendered + * @param column the column of the cell being rendered + * + * @return the component which .paint() method will paint the cell. + */ + public Component prepareRenderer(TableCellRenderer renderer, + int row, + int column) + { + boolean rowSelAllowed = getRowSelectionAllowed(); + boolean colSelAllowed = getColumnSelectionAllowed(); + boolean isSel = false; + if (rowSelAllowed && colSelAllowed || !rowSelAllowed && !colSelAllowed) + isSel = isCellSelected(row, column); + else + isSel = isRowSelected(row) && getRowSelectionAllowed() + || isColumnSelected(column) && getColumnSelectionAllowed(); + + // Determine the focused cell. The focused cell is the cell at the + // leadSelectionIndices of the row and column selection model. + ListSelectionModel rowSel = getSelectionModel(); + ListSelectionModel colSel = getColumnModel().getSelectionModel(); + boolean hasFocus = hasFocus() && isEnabled() + && rowSel.getLeadSelectionIndex() == row + && colSel.getLeadSelectionIndex() == column; + + return renderer.getTableCellRendererComponent(this, + dataModel.getValueAt(row, + convertColumnIndexToModel(column)), + isSel, + hasFocus, + row, column); + } + + + /** + * Get the value of the {@link #autoCreateColumnsFromModel} property. + * + * @return The current value of the property + */ + public boolean getAutoCreateColumnsFromModel() + { + return autoCreateColumnsFromModel; + } + + /** + * Get the value of the {@link #autoResizeMode} property. + * + * @return The current value of the property + */ + public int getAutoResizeMode() + { + return autoResizeMode; + } + + /** + * Get the value of the {@link #rowHeight} property. + * + * @return The current value of the property + */ + public int getRowHeight() + { + return rowHeight; + } + + /** + * Get the height of the specified row. + * + * @param row the row whose height to return + */ + public int getRowHeight(int row) + { + int rh = rowHeight; + if (rowHeights != null) + rh = rowHeights.getSize(row); + return rh; + } + + + /** + * Get the value of the {@link #rowMargin} property. + * + * @return The current value of the property + */ + public int getRowMargin() + { + return rowMargin; + } + + /** + * Get the value of the {@link #rowSelectionAllowed} property. + * + * @return The current value of the property + * + * @see #setRowSelectionAllowed(boolean) + */ + public boolean getRowSelectionAllowed() + { + return rowSelectionAllowed; + } + + /** + * Get the value of the {@link #cellSelectionEnabled} property. + * + * @return The current value of the property + */ + public boolean getCellSelectionEnabled() + { + return getColumnSelectionAllowed() && getRowSelectionAllowed(); + } + + /** + * Get the value of the {@link #dataModel} property. + * + * @return The current value of the property + */ + public TableModel getModel() + { + return dataModel; + } + + /** + * Get the value of the columnCount property by + * delegation to the {@link #columnModel} field. + * + * @return The current value of the columnCount property + */ + public int getColumnCount() + { + return columnModel.getColumnCount(); + } + + /** + * Get the value of the rowCount property by + * delegation to the {@link #dataModel} field. + * + * @return The current value of the rowCount property + */ + public int getRowCount() + { + return dataModel.getRowCount(); + } + + /** + * Get the value of the {@link #columnModel} property. + * + * @return The current value of the property + */ + public TableColumnModel getColumnModel() + { + return columnModel; + } + + /** + * Get the value of the selectedColumn property by + * delegation to the {@link #columnModel} field. + * + * @return The current value of the selectedColumn property + */ + public int getSelectedColumn() + { + return columnModel.getSelectionModel().getMinSelectionIndex(); + } + + private static int countSelections(ListSelectionModel lsm) + { + int lo = lsm.getMinSelectionIndex(); + int hi = lsm.getMaxSelectionIndex(); + int sum = 0; + if (lo != -1 && hi != -1) + { + switch (lsm.getSelectionMode()) + { + case ListSelectionModel.SINGLE_SELECTION: + sum = 1; + break; + + case ListSelectionModel.SINGLE_INTERVAL_SELECTION: + sum = hi - lo + 1; + break; + + case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION: + for (int i = lo; i <= hi; ++i) + if (lsm.isSelectedIndex(i)) + ++sum; + break; + } + } + return sum; + } + + private static int[] getSelections(ListSelectionModel lsm) + { + int sz = countSelections(lsm); + int [] ret = new int[sz]; + + int lo = lsm.getMinSelectionIndex(); + int hi = lsm.getMaxSelectionIndex(); + int j = 0; + if (lo != -1 && hi != -1) + { + switch (lsm.getSelectionMode()) + { + case ListSelectionModel.SINGLE_SELECTION: + ret[0] = lo; + break; + + case ListSelectionModel.SINGLE_INTERVAL_SELECTION: + for (int i = lo; i <= hi; ++i) + ret[j++] = i; + break; + + case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION: + for (int i = lo; i <= hi; ++i) + if (lsm.isSelectedIndex(i)) + ret[j++] = i; + break; + } + } + return ret; + } + + /** + * Get the value of the selectedColumnCount property by + * delegation to the {@link #columnModel} field. + * + * @return The current value of the selectedColumnCount property + */ + public int getSelectedColumnCount() + { + return countSelections(columnModel.getSelectionModel()); + } + + /** + * Get the value of the selectedColumns property by + * delegation to the {@link #columnModel} field. + * + * @return The current value of the selectedColumns property + */ + public int[] getSelectedColumns() + { + return getSelections(columnModel.getSelectionModel()); + } + + /** + * Get the value of the columnSelectionAllowed property. + * + * @return The current value of the columnSelectionAllowed property + * + * @see #setColumnSelectionAllowed(boolean) + */ + public boolean getColumnSelectionAllowed() + { + return getColumnModel().getColumnSelectionAllowed(); + } + + /** + * Get the value of the selectedRowCount property by + * delegation to the {@link #selectionModel} field. + * + * @return The current value of the selectedRowCount property + */ + public int getSelectedRowCount() + { + return countSelections(selectionModel); + } + + /** + * Get the value of the selectedRows property by + * delegation to the {@link #selectionModel} field. + * + * @return The current value of the selectedRows property + */ + public int[] getSelectedRows() + { + return getSelections(selectionModel); + } + + /** + * Get the value of the {@link #accessibleContext} property. + * + * @return The current value of the property + */ + 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; + } + + /** + * Get the value of the {@link #cellEditor} property. + * + * @return The current value of the property + */ + public TableCellEditor getCellEditor() + { + return cellEditor; + } + + /** + * Get the value of the {@link #dragEnabled} property. + * + * @return The current value of the property + */ + public boolean getDragEnabled() + { + return dragEnabled; + } + + /** + * Get the value of the {@link #gridColor} property. + * + * @return The current value of the property + */ + public Color getGridColor() + { + return gridColor; + } + + /** + * Get the value of the intercellSpacing property. + * + * @return The current value of the property + */ + public Dimension getIntercellSpacing() + { + return new Dimension(columnModel.getColumnMargin(), rowMargin); + } + + /** + * Get the value of the {@link #preferredViewportSize} property. + * + * @return The current value of the property + */ + public Dimension getPreferredScrollableViewportSize() + { + return preferredViewportSize; + } + + /** + * Get the value of the {@link #selectionBackground} property. + * + * @return The current value of the property + */ + public Color getSelectionBackground() + { + return selectionBackground; + } + + /** + * Get the value of the {@link #selectionForeground} property. + * + * @return The current value of the property + */ + public Color getSelectionForeground() + { + return selectionForeground; + } + + /** + * Get the value of the {@link #showHorizontalLines} property. + * + * @return The current value of the property + */ + public boolean getShowHorizontalLines() + { + return showHorizontalLines; + } + + /** + * Get the value of the {@link #showVerticalLines} property. + * + * @return The current value of the property + */ + public boolean getShowVerticalLines() + { + return showVerticalLines; + } + + /** + * Get the value of the {@link #tableHeader} property. + * + * @return The current value of the property + */ + public JTableHeader getTableHeader() + { + return tableHeader; + } + + /** + * Removes specified column from displayable columns of this table. + * + * @param column column to removed + */ + public void removeColumn(TableColumn column) + { + columnModel.removeColumn(column); + } + + /** + * Moves column at the specified index to new given location. + * + * @param column index of the column to move + * @param targetColumn index specifying new location of the column + */ + public void moveColumn(int column,int targetColumn) + { + columnModel.moveColumn(column, targetColumn); + } + + /** + * Set the value of the {@link #autoCreateColumnsFromModel} flag. If the + * flag changes from false to true, the + * {@link #createDefaultColumnsFromModel()} method is called. + * + * @param autoCreate the new value of the flag. + */ + public void setAutoCreateColumnsFromModel(boolean autoCreate) + { + if (autoCreateColumnsFromModel != autoCreate) + { + autoCreateColumnsFromModel = autoCreate; + if (autoCreate) + createDefaultColumnsFromModel(); + } + } + + /** + * Set the value of the {@link #autoResizeMode} property. + * + * @param a The new value of the autoResizeMode property + */ + public void setAutoResizeMode(int a) + { + autoResizeMode = a; + revalidate(); + repaint(); + } + + /** + * 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 height to set for all rows + * + * @see #getRowHeight() + * @see #setRowHeight(int, int) + * @see #getRowHeight(int) + */ + public void setRowHeight(int r) + { + if (r < 1) + throw new IllegalArgumentException(); + + clientRowHeightSet = true; + + rowHeight = r; + rowHeights = null; + revalidate(); + repaint(); + } + + /** + * Sets the height of a single row in the table. + * + * @param rh the new row height + * @param row the row to change the height of + */ + public void setRowHeight(int row, int rh) + { + if (rowHeights == null) + { + rowHeights = new SizeSequence(getRowCount(), rowHeight); + } + rowHeights.setSize(row, rh); + } + + /** + * Set the value of the {@link #rowMargin} property. + * + * @param r The new value of the rowMargin property + */ + public void setRowMargin(int r) + { + rowMargin = r; + revalidate(); + repaint(); + } + + /** + * Set the value of the {@link #rowSelectionAllowed} property. + * + * @param r The new value of the rowSelectionAllowed property + * + * @see #getRowSelectionAllowed() + */ + public void setRowSelectionAllowed(boolean r) + { + if (rowSelectionAllowed != r) + { + rowSelectionAllowed = r; + firePropertyChange("rowSelectionAllowed", !r, r); + repaint(); + } + } + + /** + * Set the value of the {@link #cellSelectionEnabled} property. + * + * @param c The new value of the cellSelectionEnabled property + */ + public void setCellSelectionEnabled(boolean c) + { + setColumnSelectionAllowed(c); + setRowSelectionAllowed(c); + // for backward-compatibility sake: + cellSelectionEnabled = true; + } + + /** + *

Set the value of the {@link #dataModel} property.

+ * + *

Unregister this as a {@link TableModelListener} from + * previous {@link #dataModel} and register it with new parameter + * m.

+ * + * @param m The new value of the model property + */ + public void setModel(TableModel m) + { + // Throw exception is m is null. + if (m == null) + throw new IllegalArgumentException(); + + // Don't do anything if setting the current model again. + if (dataModel == m) + return; + + TableModel oldModel = dataModel; + + // Remove table as TableModelListener from old model. + if (dataModel != null) + dataModel.removeTableModelListener(this); + + if (m != null) + { + // Set property. + dataModel = m; + + // 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(); + } + + // This property is bound, so we fire a property change event. + firePropertyChange("model", oldModel, dataModel); + + // Repaint table. + revalidate(); + repaint(); + } + + /** + *

Set the value of the {@link #columnModel} property.

+ * + *

Unregister this as a {@link TableColumnModelListener} + * from previous {@link #columnModel} and register it with new parameter + * c.

+ * + * @param c The new value of the columnModel property + */ + public void setColumnModel(TableColumnModel c) + { + if (c == null) + throw new IllegalArgumentException(); + TableColumnModel tmp = columnModel; + if (tmp != null) + tmp.removeColumnModelListener(this); + if (c != null) + c.addColumnModelListener(this); + columnModel = c; + if (dataModel != null && columnModel != null) + { + int ncols = getColumnCount(); + TableColumn column; + for (int i = 0; i < ncols; ++i) + { + column = columnModel.getColumn(i); + if (column.getHeaderValue()==null) + column.setHeaderValue(dataModel.getColumnName(i)); + } + } + + // according to Sun's spec we also have to set the tableHeader's + // column model here + if (tableHeader != null) + tableHeader.setColumnModel(c); + + revalidate(); + repaint(); + } + + /** + * Set the value of the columnSelectionAllowed property. + * + * @param c The new value of the property + * + * @see #getColumnSelectionAllowed() + */ + public void setColumnSelectionAllowed(boolean c) + { + if (columnModel.getColumnSelectionAllowed() != c) + { + columnModel.setColumnSelectionAllowed(c); + firePropertyChange("columnSelectionAllowed", !c, c); + repaint(); + } + } + + /** + *

Set the value of the {@link #selectionModel} property.

+ * + *

Unregister this as a {@link ListSelectionListener} + * from previous {@link #selectionModel} and register it with new + * parameter s.

+ * + * @param s The new value of the selectionModel property + */ + public void setSelectionModel(ListSelectionModel s) + { + if (s == null) + throw new IllegalArgumentException(); + ListSelectionModel tmp = selectionModel; + if (tmp != null) + tmp.removeListSelectionListener(this); + if (s != null) + s.addListSelectionListener(this); + selectionModel = s; + checkSelection(); + } + + /** + * Set the value of the selectionMode property by + * delegation to the {@link #selectionModel} field. The same selection + * mode is set for row and column selection models. + * + * @param s The new value of the property + */ + public void setSelectionMode(int s) + { + selectionModel.setSelectionMode(s); + columnModel.getSelectionModel().setSelectionMode(s); + + repaint(); + } + + /** + *

Set the value of the {@link #cellEditor} property.

+ * + *

Unregister this as a {@link CellEditorListener} from + * previous {@link #cellEditor} and register it with new parameter + * c.

+ * + * @param c The new value of the cellEditor property + */ + public void setCellEditor(TableCellEditor c) + { + TableCellEditor tmp = cellEditor; + if (tmp != null) + tmp.removeCellEditorListener(this); + if (c != null) + c.addCellEditorListener(this); + cellEditor = c; + } + + /** + * Set the value of the {@link #dragEnabled} property. + * + * @param d The new value of the dragEnabled property + */ + public void setDragEnabled(boolean d) + { + dragEnabled = d; + } + + /** + * Set the value of the {@link #gridColor} property. + * + * @param g The new value of the gridColor property + */ + public void setGridColor(Color g) + { + gridColor = g; + repaint(); + } + + /** + * Set the value of the intercellSpacing property. + * + * @param i The new value of the intercellSpacing property + */ + public void setIntercellSpacing(Dimension i) + { + rowMargin = i.height; + columnModel.setColumnMargin(i.width); + repaint(); + } + + /** + * Set the value of the {@link #preferredViewportSize} property. + * + * @param p The new value of the preferredViewportSize property + */ + public void setPreferredScrollableViewportSize(Dimension p) + { + preferredViewportSize = p; + revalidate(); + repaint(); + } + + /** + *

Set the value of the {@link #selectionBackground} property.

+ * + *

Fire a PropertyChangeEvent with name {@link + * #SELECTION_BACKGROUND_CHANGED_PROPERTY} to registered listeners, if + * selectionBackground changed.

+ * + * @param s The new value of the selectionBackground property + */ + public void setSelectionBackground(Color s) + { + Color tmp = selectionBackground; + selectionBackground = s; + if (((tmp == null && s != null) + || (s == null && tmp != null) + || (tmp != null && s != null && !tmp.equals(s)))) + firePropertyChange(SELECTION_BACKGROUND_CHANGED_PROPERTY, tmp, s); + repaint(); + } + + /** + *

Set the value of the {@link #selectionForeground} property.

+ * + *

Fire a PropertyChangeEvent with name {@link + * #SELECTION_FOREGROUND_CHANGED_PROPERTY} to registered listeners, if + * selectionForeground changed.

+ * + * @param s The new value of the selectionForeground property + */ + public void setSelectionForeground(Color s) + { + Color tmp = selectionForeground; + selectionForeground = s; + if (((tmp == null && s != null) + || (s == null && tmp != null) + || (tmp != null && s != null && !tmp.equals(s)))) + firePropertyChange(SELECTION_FOREGROUND_CHANGED_PROPERTY, tmp, s); + repaint(); + } + + /** + * Set the value of the showGrid property. + * + * @param s The new value of the showGrid property + */ + public void setShowGrid(boolean s) + { + setShowVerticalLines(s); + setShowHorizontalLines(s); + } + + /** + * Set the value of the {@link #showHorizontalLines} property. + * + * @param s The new value of the showHorizontalLines property + */ + public void setShowHorizontalLines(boolean s) + { + showHorizontalLines = s; + repaint(); + } + + /** + * Set the value of the {@link #showVerticalLines} property. + * + * @param s The new value of the showVerticalLines property + */ + public void setShowVerticalLines(boolean s) + { + showVerticalLines = s; + repaint(); + } + + /** + * Set the value of the {@link #tableHeader} property. + * + * @param t The new value of the tableHeader property + */ + public void setTableHeader(JTableHeader t) + { + if (tableHeader != null) + tableHeader.setTable(null); + tableHeader = t; + if (tableHeader != null) + tableHeader.setTable(this); + revalidate(); + repaint(); + } + + protected void configureEnclosingScrollPane() + { + JScrollPane jsp = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, this); + if (jsp != null && tableHeader != null) + { + jsp.setColumnHeaderView(tableHeader); + } + } + + protected void unconfigureEnclosingScrollPane() + { + JScrollPane jsp = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, this); + if (jsp != null) + { + jsp.setColumnHeaderView(null); + } + } + + + public void addNotify() + { + super.addNotify(); + configureEnclosingScrollPane(); + } + + public void removeNotify() + { + super.addNotify(); + unconfigureEnclosingScrollPane(); + } + + + /** + * This distributes the superfluous width in a table evenly on its columns. + * + * The implementation used here is different to that one described in + * the JavaDocs. It is much simpler, and seems to work very well. + * + * TODO: correctly implement the algorithm described in the JavaDoc + */ + private void distributeSpill(TableColumn[] cols, int spill) + { + int average = spill / cols.length; + for (int i = 0; i < cols.length; i++) + { + if (cols[i] != null) + cols[i].setWidth(cols[i].getPreferredWidth() + average); + } + } + + /** + * This distributes the superfluous width in a table, setting the width of the + * column being resized strictly to its preferred width. + */ + private void distributeSpillResizing(TableColumn[] cols, int spill, + TableColumn resizeIt) + { + int average = 0; + if (cols.length != 1) + average = spill / (cols.length-1); + for (int i = 0; i < cols.length; i++) + { + if (cols[i] != null && !cols[i].equals(resizeIt)) + cols[i].setWidth(cols[i].getPreferredWidth() + average); + } + resizeIt.setWidth(resizeIt.getPreferredWidth()); + } + + /** + * Set the widths of all columns, taking they preferred widths into + * consideration. The excess space, if any, will be distrubuted between + * all columns. This method also handles special cases when one of the + * collumns is currently being resized. + * + * @see TableColumn#setPreferredWidth(int) + */ + public void doLayout() + { + TableColumn resizingColumn = null; + + int ncols = columnModel.getColumnCount(); + if (ncols < 1) + return; + + int prefSum = 0; + int rCol = -1; + + if (tableHeader != null) + resizingColumn = tableHeader.getResizingColumn(); + + for (int i = 0; i < ncols; ++i) + { + TableColumn col = columnModel.getColumn(i); + int p = col.getPreferredWidth(); + prefSum += p; + if (resizingColumn == col) + rCol = i; + } + + int spill = getWidth() - prefSum; + + if (resizingColumn != null) + { + TableColumn col; + TableColumn [] cols; + + switch (getAutoResizeMode()) + { + case AUTO_RESIZE_LAST_COLUMN: + col = columnModel.getColumn(ncols-1); + col.setWidth(col.getPreferredWidth() + spill); + break; + + case AUTO_RESIZE_NEXT_COLUMN: + col = columnModel.getColumn(ncols-1); + col.setWidth(col.getPreferredWidth() + spill); + break; + + case AUTO_RESIZE_ALL_COLUMNS: + cols = new TableColumn[ncols]; + for (int i = 0; i < ncols; ++i) + cols[i] = columnModel.getColumn(i); + distributeSpillResizing(cols, spill, resizingColumn); + break; + + case AUTO_RESIZE_SUBSEQUENT_COLUMNS: + + // Subtract the width of the non-resized columns from the spill. + int w = 0; + int wp = 0; + TableColumn column; + for (int i = 0; i < rCol; i++) + { + column = columnModel.getColumn(i); + w += column.getWidth(); + wp+= column.getPreferredWidth(); + } + + // The number of columns right from the column being resized. + int n = ncols-rCol-1; + if (n>0) + { + // If there are any columns on the right sied to resize. + spill = (getWidth()-w) - (prefSum-wp); + int average = spill / n; + + // For all columns right from the column being resized: + for (int i = rCol+1; i < ncols; i++) + { + column = columnModel.getColumn(i); + column.setWidth(column.getPreferredWidth() + average); + } + } + resizingColumn.setWidth(resizingColumn.getPreferredWidth()); + break; + + case AUTO_RESIZE_OFF: + default: + int prefWidth = resizingColumn.getPreferredWidth(); + resizingColumn.setWidth(prefWidth); + } + } + else + { + TableColumn[] cols = new TableColumn[ncols]; + + for (int i = 0; i < ncols; ++i) + cols[i] = columnModel.getColumn(i); + + distributeSpill(cols, spill); + } + + if (editorComp!=null) + moveToCellBeingEdited(editorComp); + + 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 doLayout() + */ + public void sizeColumnsToFit(boolean lastColumnOnly) + { + doLayout(); + } + + /** + * Obsolete since JDK 1.4. Please use doLayout(). + */ + public void sizeColumnsToFit(int resizingColumn) + { + doLayout(); + } + + public String getUIClassID() + { + return "TableUI"; + } + + /** + * This method returns the table's UI delegate. + * + * @return The table's UI delegate. + */ + public TableUI getUI() + { + return (TableUI) ui; + } + + /** + * This method sets the table's UI delegate. + * + * @param ui The table's UI delegate. + */ + 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() + { + setUI((TableUI) UIManager.getUI(this)); + } + + /** + * Get the class (datatype) of the column. The cells are rendered and edited + * differently, depending from they data type. + * + * @param column the column (not the model index). + * + * @return the class, defining data type of that column (String.class for + * String, Boolean.class for boolean and so on). + */ + public Class getColumnClass(int column) + { + return getModel().getColumnClass(convertColumnIndexToModel(column)); + } + + /** + * Get the name of the column. If the column has the column identifier set, + * the return value is the result of the .toString() method call on that + * identifier. If the identifier is not explicitly set, the returned value + * is calculated by + * {@link javax.swing.table.AbstractTableModel#getColumnName(int)}. + * + * @param column the column + * + * @return the name of that column. + */ + public String getColumnName(int column) + { + int modelColumn = columnModel.getColumn(column).getModelIndex(); + return dataModel.getColumnName(modelColumn); + } + + /** + * Get the column, currently being edited + * + * @return the column, currently being edited. + */ + public int getEditingColumn() + { + return editingColumn; + } + + /** + * Set the column, currently being edited + * + * @param column the column, currently being edited. + */ + public void setEditingColumn(int column) + { + editingColumn = column; + } + + /** + * Get the row currently being edited. + * + * @return the row, currently being edited. + */ + public int getEditingRow() + { + return editingRow; + } + + /** + * Set the row currently being edited. + * + * @param row the row, that will be edited + */ + public void setEditingRow(int row) + { + editingRow = row; + } + + /** + * Get the editor component that is currently editing one of the cells + * + * @return the editor component or null, if none of the cells is being + * edited. + */ + public Component getEditorComponent() + { + return editorComp; + } + + /** + * Check if one of the table cells is currently being edited. + * + * @return true if there is a cell being edited. + */ + public boolean isEditing() + { + return editorComp != null; + } + + /** + * Set the default editor for the given column class (column data type). + * By default, String is handled by text field and Boolean is handled by + * the check box. + * + * @param columnClass the column data type + * @param editor the editor that will edit this data type + * + * @see TableModel#getColumnClass(int) + */ + public void setDefaultEditor(Class columnClass, TableCellEditor editor) + { + if (editor != null) + defaultEditorsByColumnClass.put(columnClass, editor); + else + defaultEditorsByColumnClass.remove(columnClass); + } + + public void addColumnSelectionInterval(int index0, int index1) + { + if ((index0 < 0 || index0 > (getColumnCount()-1) + || index1 < 0 || index1 > (getColumnCount()-1))) + throw new IllegalArgumentException("Column index out of range."); + + getColumnModel().getSelectionModel().addSelectionInterval(index0, index1); + } + + public void addRowSelectionInterval(int index0, int index1) + { + if ((index0 < 0 || index0 > (getRowCount()-1) + || index1 < 0 || index1 > (getRowCount()-1))) + throw new IllegalArgumentException("Row index out of range."); + + getSelectionModel().addSelectionInterval(index0, index1); + } + + public void setColumnSelectionInterval(int index0, int index1) + { + if ((index0 < 0 || index0 > (getColumnCount()-1) + || index1 < 0 || index1 > (getColumnCount()-1))) + throw new IllegalArgumentException("Column index out of range."); + + getColumnModel().getSelectionModel().setSelectionInterval(index0, index1); + } + + public void setRowSelectionInterval(int index0, int index1) + { + if ((index0 < 0 || index0 > (getRowCount()-1) + || index1 < 0 || index1 > (getRowCount()-1))) + throw new IllegalArgumentException("Row index out of range."); + + getSelectionModel().setSelectionInterval(index0, index1); + } + + public void removeColumnSelectionInterval(int index0, int index1) + { + if ((index0 < 0 || index0 > (getColumnCount()-1) + || index1 < 0 || index1 > (getColumnCount()-1))) + throw new IllegalArgumentException("Column index out of range."); + + getColumnModel().getSelectionModel().removeSelectionInterval(index0, index1); + } + + public void removeRowSelectionInterval(int index0, int index1) + { + if ((index0 < 0 || index0 > (getRowCount()-1) + || index1 < 0 || index1 > (getRowCount()-1))) + throw new IllegalArgumentException("Row index out of range."); + + getSelectionModel().removeSelectionInterval(index0, index1); + } + + /** + * Checks if the given column is selected. + * + * @param column the column + * + * @return true if the column is selected (as reported by the selection + * model, associated with the column model), false otherwise. + */ + public boolean isColumnSelected(int column) + { + return getColumnModel().getSelectionModel().isSelectedIndex(column); + } + + /** + * Checks if the given row is selected. + * + * @param row the row + * + * @return true if the row is selected (as reported by the selection model), + * false otherwise. + */ + public boolean isRowSelected(int row) + { + return getSelectionModel().isSelectedIndex(row); + } + + /** + * Checks if the given cell is selected. The cell is selected if both + * the cell row and the cell column are selected. + * + * @param row the cell row + * @param column the cell column + * + * @return true if the cell is selected, false otherwise + */ + public boolean isCellSelected(int row, int column) + { + return isRowSelected(row) && isColumnSelected(column); + } + + /** + * Select all table. + */ + 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(); + // the following calls to setSelectionInterval change the lead selection + // indices + setColumnSelectionInterval(0, getColumnCount() - 1); + setRowSelectionInterval(0, getRowCount() - 1); + // the following addSelectionInterval calls restore the lead selection + // indices to their previous values + addColumnSelectionInterval(colLead,colLead); + addRowSelectionInterval(rowLead, rowLead); + } + + /** + * Get the cell value at the given position. + * + * @param row the row to get the value + * @param column the actual column number (not the model index) + * to get the value. + * + * @return the cell value, as returned by model. + */ + public Object getValueAt(int row, int column) + { + return dataModel.getValueAt(row, convertColumnIndexToModel(column)); + } + + /** + * Set value for the cell at the given position. The modified cell is + * repainted. + * + * @param value the value to set + * @param row the row of the cell being modified + * @param column the column of the cell being modified + */ + public void setValueAt(Object value, int row, int column) + { + dataModel.setValueAt(value, row, convertColumnIndexToModel(column)); + + repaint(getCellRect(row, column, true)); + } + + /** + * Get table column with the given identified. + * + * @param identifier the column identifier + * + * @return the table column with this identifier + * + * @throws IllegalArgumentException if identifier is + * null or there is no column with that identifier. + * + * @see TableColumn#setIdentifier(Object) + */ + public TableColumn getColumn(Object identifier) + { + return columnModel.getColumn(columnModel.getColumnIndex(identifier)); + } + + /** + * Returns true if the specified cell is editable, and + * false otherwise. + * + * @param row the row index. + * @param column the column index. + * + * @return true if the cell is editable, false otherwise. + */ + public boolean isCellEditable(int row, int column) + { + return dataModel.isCellEditable(row, convertColumnIndexToModel(column)); + } + + /** + * Clears any existing columns from the JTable's + * {@link TableColumnModel} and creates new columns to match the values in + * the data ({@link TableModel}) used by the table. + * + * @see #setAutoCreateColumnsFromModel(boolean) + */ + public void createDefaultColumnsFromModel() + { + assert columnModel != null : "The columnModel must not be null."; + + // remove existing columns + int columnIndex = columnModel.getColumnCount() - 1; + while (columnIndex >= 0) + { + columnModel.removeColumn(columnModel.getColumn(columnIndex)); + columnIndex--; + } + + // add new columns to match the TableModel + int columnCount = dataModel.getColumnCount(); + for (int c = 0; c < columnCount; c++) + { + TableColumn column = new TableColumn(c); + column.setIdentifier(dataModel.getColumnName(c)); + column.setHeaderValue(dataModel.getColumnName(c)); + columnModel.addColumn(column); + column.addPropertyChangeListener(tableColumnPropertyChangeHandler); + } + } + + public void changeSelection (int rowIndex, int columnIndex, boolean toggle, boolean extend) + { + if (toggle && extend) + { + // Leave the selection state as is, but move the anchor + // index to the specified location + selectionModel.setAnchorSelectionIndex(rowIndex); + getColumnModel().getSelectionModel().setAnchorSelectionIndex(columnIndex); + } + else if (toggle) + { + // Toggle the state of the specified cell + if (isCellSelected(rowIndex,columnIndex)) + { + selectionModel.removeSelectionInterval(rowIndex,rowIndex); + getColumnModel().getSelectionModel().removeSelectionInterval(columnIndex,columnIndex); + } + else + { + selectionModel.addSelectionInterval(rowIndex,rowIndex); + getColumnModel().getSelectionModel().addSelectionInterval(columnIndex,columnIndex); + } + } + else if (extend) + { + // Extend the previous selection from the anchor to the + // specified cell, clearing all other selections + selectionModel.setLeadSelectionIndex(rowIndex); + getColumnModel().getSelectionModel().setLeadSelectionIndex(columnIndex); + } + else + { + // Clear the previous selection and ensure the new cell + // is selected + selectionModel.clearSelection(); + selectionModel.setSelectionInterval(rowIndex,rowIndex); + getColumnModel().getSelectionModel().clearSelection(); + getColumnModel().getSelectionModel().setSelectionInterval(columnIndex, columnIndex); + + + } + } + + /** + * 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) + { + // Complete the previous editing session, if still active. + if (isEditing()) + editingStopped(new ChangeEvent("editingStopped")); + + TableCellEditor editor = getCellEditor(row, column); + + // 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); + } + } + + /** + * Move the given component under the cell being edited. + * The table must be in the editing mode. + * + * @param component the component to move. + */ + private void moveToCellBeingEdited(Component component) + { + Rectangle r = getCellRect(editingRow, editingColumn, true); + // Adjust bounding box of the editing component, so that it lies + // 'above' the grid on all edges, not only right and bottom. + // The table grid is painted only at the right and bottom edge of a cell. + r.x -= 1; + r.y -= 1; + r.width += 1; + r.height += 1; + component.setBounds(r); + } + + /** + * 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, EventObject e) + { + return editCellAt(row, column); + } + + /** + * Discards the editor object. + */ + public void removeEditor() + { + editingStopped(new ChangeEvent(this)); + } + + /** + * Prepares the editor by querying for the value and selection state of the + * cell at (row, column). + * + * @param editor the TableCellEditor to set up + * @param row the row of the cell to edit + * @param column the column of the cell to edit + * @return the Component being edited + */ + public Component prepareEditor (TableCellEditor editor, int row, int column) + { + return editor.getTableCellEditorComponent + (this, getValueAt(row, column), isCellSelected(row, column), row, column); + } + + /** + * This revalidates the JTable and queues a repaint. + */ + protected void resizeAndRepaint() + { + revalidate(); + repaint(); + } + + /** + * Sets whether cell editors of this table should receive keyboard focus + * when the editor is activated by a keystroke. The default setting is + * false which means that the table should keep the keyboard + * focus until the cell is selected by a mouse click. + * + * @param value the value to set + * + * @since 1.4 + */ + public void setSurrendersFocusOnKeystroke(boolean value) + { + // TODO: Implement functionality of this property (in UI impl). + surrendersFocusOnKeystroke = value; + } + + /** + * Returns whether cell editors of this table should receive keyboard focus + * when the editor is activated by a keystroke. The default setting is + * false which means that the table should keep the keyboard + * focus until the cell is selected by a mouse click. + * + * @return whether cell editors of this table should receive keyboard focus + * when the editor is activated by a keystroke + * + * @since 1.4 + */ + public boolean getSurrendersFocusOnKeystroke() + { + // TODO: Implement functionality of this property (in UI impl). + return surrendersFocusOnKeystroke; + } + + /** + * Helper method for + * {@link LookAndFeel#installProperty(JComponent, String, Object)}. + * + * @param propertyName the name of the property + * @param value the value of the property + * + * @throws IllegalArgumentException if the specified property cannot be set + * by this method + * @throws ClassCastException if the property value does not match the + * property type + * @throws NullPointerException if c or + * propertyValue is null + */ + void setUIProperty(String propertyName, Object value) + { + if (propertyName.equals("rowHeight")) + { + if (! clientRowHeightSet) + { + setRowHeight(((Integer) value).intValue()); + clientRowHeightSet = false; + } + } + else + { + super.setUIProperty(propertyName, value); + } + } +} -- cgit v1.2.3