2 Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
4 This file is part of GNU Classpath.
6 GNU Classpath is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
11 GNU Classpath is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with GNU Classpath; see the file COPYING. If not, write to the
18 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 Linking this library statically or dynamically with other modules is
22 making a combined work based on this library. Thus, the terms and
23 conditions of the GNU General Public License cover the whole
26 As a special exception, the copyright holders of this library give you
27 permission to link this library with independent modules to produce an
28 executable, regardless of the license terms of these independent
29 modules, and to copy and distribute the resulting executable under
30 terms of your choice, provided that you also meet, for each linked
31 independent module, the terms and conditions of the license of that
32 module. An independent module is a module which is not derived from
33 or based on this library. If you modify this library, you may extend
34 this exception to your version of the library, but you are not
35 obligated to do so. If you do not wish to do so, delete this
36 exception statement from your version. */
41 import java.awt.Color;
42 import java.awt.Component;
43 import java.awt.Cursor;
44 import java.awt.Dimension;
46 import java.awt.FontMetrics;
47 import java.awt.Point;
48 import java.awt.Rectangle;
49 import java.awt.event.FocusListener;
50 import java.beans.PropertyChangeEvent;
51 import java.beans.PropertyChangeListener;
52 import java.text.DateFormat;
53 import java.text.NumberFormat;
54 import java.util.Date;
55 import java.util.EventObject;
56 import java.util.Hashtable;
57 import java.util.Locale;
58 import java.util.Vector;
60 import javax.accessibility.Accessible;
61 import javax.accessibility.AccessibleComponent;
62 import javax.accessibility.AccessibleContext;
63 import javax.accessibility.AccessibleExtendedTable;
64 import javax.accessibility.AccessibleRole;
65 import javax.accessibility.AccessibleSelection;
66 import javax.accessibility.AccessibleState;
67 import javax.accessibility.AccessibleStateSet;
68 import javax.accessibility.AccessibleTable;
69 import javax.accessibility.AccessibleTableModelChange;
70 import javax.swing.event.CellEditorListener;
71 import javax.swing.event.ChangeEvent;
72 import javax.swing.event.ListSelectionEvent;
73 import javax.swing.event.ListSelectionListener;
74 import javax.swing.event.TableColumnModelEvent;
75 import javax.swing.event.TableColumnModelListener;
76 import javax.swing.event.TableModelEvent;
77 import javax.swing.event.TableModelListener;
78 import javax.swing.plaf.TableUI;
79 import javax.swing.table.DefaultTableCellRenderer;
80 import javax.swing.table.DefaultTableColumnModel;
81 import javax.swing.table.DefaultTableModel;
82 import javax.swing.table.JTableHeader;
83 import javax.swing.table.TableCellEditor;
84 import javax.swing.table.TableCellRenderer;
85 import javax.swing.table.TableColumn;
86 import javax.swing.table.TableColumnModel;
87 import javax.swing.table.TableModel;
90 * The table component, displaying information, organized in rows and columns.
91 * The table can be placed in the scroll bar and have the optional header
92 * that is always visible. Cell values may be editable after double clicking
93 * on the cell. Cell columns may have various data types, that are
94 * displayed and edited by the different renderers and editors. It is possible
95 * to set different column width. The columns are also resizeable by
96 * dragging the column boundary in the header.
100 implements TableModelListener, Scrollable, TableColumnModelListener,
101 ListSelectionListener, CellEditorListener, Accessible
104 * Provides accessibility support for <code>JTable</code>.
106 * @author Roman Kennke (kennke@aicas.com)
108 protected class AccessibleJTable
109 extends AccessibleJComponent
110 implements AccessibleSelection, ListSelectionListener, TableModelListener,
111 TableColumnModelListener, CellEditorListener, PropertyChangeListener,
112 AccessibleExtendedTable
116 * Provides accessibility support for table cells.
118 * @author Roman Kennke (kennke@aicas.com)
120 protected class AccessibleJTableCell
121 extends AccessibleContext
122 implements Accessible, AccessibleComponent
126 * The table of this cell.
128 private JTable table;
131 * The row index of this cell.
136 * The column index of this cell.
141 * The index of this cell inside the AccessibleJTable parent.
146 * Creates a new <code>AccessibleJTableCell</code>.
150 * @param c the column
151 * @param i the index of this cell inside the accessible table parent
153 public AccessibleJTableCell(JTable t, int r, int c, int i)
162 * Returns the accessible row for the table cell.
164 * @return the accessible row for the table cell
166 public AccessibleRole getAccessibleRole()
168 // TODO: What is the role of the table cell?
169 // Seems like the RI returns UNKNOWN here for 'normal' cells, might
170 // be different for special renderers though (not tested yet).
171 return AccessibleRole.UNKNOWN;
175 * Returns the accessible state set of this accessible table cell.
177 * @return the accessible state set of this accessible table cell
179 public AccessibleStateSet getAccessibleStateSet()
181 AccessibleStateSet state = new AccessibleStateSet();
183 // Figure out the SHOWING state.
184 Rectangle visibleRect = getVisibleRect();
185 Rectangle cellRect = getCellRect(row, column, false);
186 if (visibleRect.intersects(cellRect))
187 state.add(AccessibleState.SHOWING);
189 // Figure out SELECTED state.
190 if (isCellSelected(row, column))
191 state.add(AccessibleState.SELECTED);
193 // Figure out ACTIVE state.
194 if (row == getSelectedRow() && column == getSelectedColumn())
195 state.add(AccessibleState.ACTIVE);
197 // TRANSIENT seems to be always set in the RI.
198 state.add(AccessibleState.TRANSIENT);
200 // TODO: Any other state to handle here?
205 * Returns the index of this cell in the parent object.
207 * @return the index of this cell in the parent object
209 public int getAccessibleIndexInParent()
215 * Returns the number of children of this object. Table cells cannot have
216 * children, so we return <code>0</code> here.
218 * @return <code>0</code>
220 public int getAccessibleChildrenCount()
226 * Returns the accessible child at index <code>i</code>. Table cells
227 * don't have children, so we return <code>null</code> here.
229 * @return <code>null</code>
231 public Accessible getAccessibleChild(int i)
237 * Returns the locale setting for this accessible table cell.
239 * @return the locale setting for this accessible table cell
241 public Locale getLocale()
243 // TODO: For now, we return english here. This must be fixed as soon
244 // as we have a localized Swing.
245 return Locale.ENGLISH;
249 * Returns the accessible context of this table cell. Since accessible
250 * table cells are their own accessible context, we return
253 * @return the accessible context of this table cell
255 public AccessibleContext getAccessibleContext()
261 * Returns the background color of this cell.
263 * @return the background color of this cell
265 public Color getBackground()
267 return table.getBackground();
271 * Sets the background of the cell. Since table cells cannot have
272 * individual background colors, this method does nothing. Set the
273 * background directly on the table instead.
275 * @param color not used
277 public void setBackground(Color color)
279 // This method does nothing. See API comments.
283 * Returns the foreground color of the table cell.
285 * @return the foreground color of the table cell
287 public Color getForeground()
289 return table.getForeground();
293 * Sets the foreground of the cell. Since table cells cannot have
294 * individual foreground colors, this method does nothing. Set the
295 * foreground directly on the table instead.
297 * @param color not used
299 public void setForeground(Color color)
301 // This method does nothing. See API comments.
305 * Returns the cursor for this table cell.
307 * @return the cursor for this table cell
309 public Cursor getCursor()
311 return table.getCursor();
315 * Sets the cursor of the cell. Since table cells cannot have
316 * individual cursors, this method does nothing. Set the
317 * cursor directly on the table instead.
319 * @param cursor not used
321 public void setCursor(Cursor cursor)
323 // This method does nothing. See API comments.
327 * Returns the font of the table cell.
329 * @return the font of the table cell
331 public Font getFont()
333 return table.getFont();
337 * Sets the font of the cell. Since table cells cannot have
338 * individual fonts, this method does nothing. Set the
339 * font directly on the table instead.
341 * @param font not used
343 public void setFont(Font font)
345 // This method does nothing. See API comments.
349 * Returns the font metrics for a specified font.
351 * @param font the font for which we return the metrics
353 * @return the font metrics for a specified font
355 public FontMetrics getFontMetrics(Font font)
357 return table.getFontMetrics(font);
361 * Returns <code>true</code> if this table cell is enabled,
362 * <code>false</code> otherwise.
364 * @return <code>true</code> if this table cell is enabled,
365 * <code>false</code> otherwise
367 public boolean isEnabled()
369 return table.isEnabled();
373 * Table cells cannot be disabled or enabled individually, so this method
374 * does nothing. Set the enabled flag on the table itself.
376 * @param b not used here
378 public void setEnabled(boolean b)
380 // This method does nothing. See API comments.
384 * Returns <code>true</code> if this cell is visible, <code>false</code>
387 * @return <code>true</code> if this cell is visible, <code>false</code>
390 public boolean isVisible()
392 return table.isVisible();
396 * The visibility cannot be set on individual table cells, so this method
397 * does nothing. Set the visibility on the table itself.
401 public void setVisible(boolean b)
403 // This method does nothing. See API comments.
407 * Returns <code>true</code> if this table cell is currently showing on
410 * @return <code>true</code> if this table cell is currently showing on
413 public boolean isShowing()
415 return table.isShowing();
419 * Returns <code>true</code> if this table cell contains the location
420 * at <code>point</code>, <code>false</code> otherwise.
421 * <code>point</code> is interpreted as relative to the coordinate system
424 * @return <code>true</code> if this table cell contains the location
425 * at <code>point</code>, <code>false</code> otherwise
427 public boolean contains(Point point)
429 Rectangle cellRect = table.getCellRect(row, column, true);
432 return cellRect.contains(point);
436 * Returns the screen location of the table cell.
438 * @return the screen location of the table cell
440 public Point getLocationOnScreen()
442 Point tableLoc = table.getLocationOnScreen();
443 Rectangle cellRect = table.getCellRect(row, column, true);
444 tableLoc.x += cellRect.x;
445 tableLoc.y += cellRect.y;
450 * Returns the location of this cell relative to the table's bounds.
452 * @return the location of this cell relative to the table's bounds
454 public Point getLocation()
456 Rectangle cellRect = table.getCellRect(row, column, true);
457 return new Point(cellRect.x, cellRect.y);
461 * The location of the table cells cannot be manipulated directly, so
462 * this method does nothing.
464 * @param point not used
466 public void setLocation(Point point)
468 // This method does nothing. See API comments.
472 * Returns the bounds of the cell relative to its table.
474 * @return the bounds of the cell relative to its table
476 public Rectangle getBounds()
478 return table.getCellRect(row, column, true);
482 * The bounds of the table cells cannot be manipulated directly, so
483 * this method does nothing.
485 * @param rectangle not used
487 public void setBounds(Rectangle rectangle)
489 // This method does nothing. See API comments.
493 * Returns the size of the table cell.
495 * @return the size of the table cell
497 public Dimension getSize()
499 Rectangle cellRect = table.getCellRect(row, column, true);
500 return new Dimension(cellRect.width, cellRect.height);
504 * The size cannot be set on table cells directly, so this method does
507 * @param dimension not used
509 public void setSize(Dimension dimension)
511 // This method does nothing. See API comments.
515 * Table cells have no children, so we return <code>null</code> here.
517 * @return <code>null</code>
519 public Accessible getAccessibleAt(Point point)
525 * Returns <code>true</code> if this table cell is focus traversable,
526 * <code>false</code> otherwise.
528 * @return <code>true</code> if this table cell is focus traversable,
529 * <code>false</code> otherwise
531 public boolean isFocusTraversable()
533 return table.isFocusable();
537 * Requests that this table cell gets the keyboard focus.
539 public void requestFocus()
541 // We first set the selection models' lead selection to this cell.
542 table.getColumnModel().getSelectionModel()
543 .setLeadSelectionIndex(column);
544 table.getSelectionModel().setLeadSelectionIndex(row);
545 // Now we request that the table receives focus.
546 table.requestFocus();
550 * Adds a focus listener to this cell. The focus listener is really
551 * added to the table, so there is no way to find out when an individual
552 * cell changes the focus.
554 * @param listener the focus listener to add
556 public void addFocusListener(FocusListener listener)
558 table.addFocusListener(listener);
562 * Removes a focus listener from the cell. The focus listener is really
563 * removed from the table.
565 * @param listener the listener to remove
567 public void removeFocusListener(FocusListener listener)
569 table.removeFocusListener(listener);
574 protected class AccessibleJTableModelChange
575 implements AccessibleTableModelChange
578 protected int firstRow;
579 protected int lastRow;
580 protected int firstColumn;
581 protected int lastColumn;
583 protected AccessibleJTableModelChange(int type, int firstRow,
584 int lastRow, int firstColumn,
588 this.firstRow = firstRow;
589 this.lastRow = lastRow;
590 this.firstColumn = firstColumn;
591 this.lastColumn = lastColumn;
599 public int getFirstRow()
604 public int getLastRow()
609 public int getFirstColumn()
614 public int getLastColumn()
621 * The RI returns an instance with this name in
622 * {@link #getAccessibleColumnHeader()}, this makes sense, so we do the
625 private class AccessibleTableHeader
626 implements AccessibleTable
630 * The JTableHeader wrapped by this class.
632 private JTableHeader header;
635 * Creates a new instance.
637 * @param h the JTableHeader to wrap
639 private AccessibleTableHeader(JTableHeader h)
645 * Returns the caption for the table header.
647 * @return the caption for the table header
649 public Accessible getAccessibleCaption()
651 // The RI seems to always return null here, so do we.
656 * Sets the caption for the table header.
658 * @param caption the caption to set
660 public void setAccessibleCaption(Accessible caption)
662 // This seems to be a no-op in the RI, so we do the same.
666 * Returns the caption for the table header.
668 * @return the caption for the table header
670 public Accessible getAccessibleSummary()
672 // The RI seems to always return null here, so do we.
677 * Sets the summary for the table header.
679 * @param summary the caption to set
681 public void setAccessibleSummary(Accessible summary)
683 // This seems to be a no-op in the RI, so we do the same.
687 * Returns the number of rows, which is always 1 for the table header.
689 * @return the number of rows
691 public int getAccessibleRowCount()
697 * Returns the number of columns in the table header.
699 * @return the number of columns in the table header
701 public int getAccessibleColumnCount()
703 return header.getColumnModel().getColumnCount();
707 * Returns the accessible child at the specified row and column.
708 * The row number is ignored here, and we return an
709 * AccessibleJTableHeaderCell here with the renderer component as
712 * @param r the row number
713 * @param c the column number
715 * @return the accessible child at the specified row and column
717 public Accessible getAccessibleAt(int r, int c)
719 TableColumn column = header.getColumnModel().getColumn(c);
720 TableCellRenderer rend = column.getHeaderRenderer();
722 rend = header.getDefaultRenderer();
724 rend.getTableCellRendererComponent(header.getTable(),
725 column.getHeaderValue(), false,
727 return new AccessibleJTableHeaderCell(header, comp, r, c);
730 public int getAccessibleRowExtentAt(int r, int c)
732 // TODO Auto-generated method stub
736 public int getAccessibleColumnExtentAt(int r, int c)
738 // TODO Auto-generated method stub
742 public AccessibleTable getAccessibleRowHeader()
744 // TODO Auto-generated method stub
748 public void setAccessibleRowHeader(AccessibleTable header)
750 // TODO Auto-generated method stub
754 public AccessibleTable getAccessibleColumnHeader()
756 // TODO Auto-generated method stub
760 public void setAccessibleColumnHeader(AccessibleTable header)
762 // TODO Auto-generated method stub
766 public Accessible getAccessibleRowDescription(int r)
768 // TODO Auto-generated method stub
772 public void setAccessibleRowDescription(int r, Accessible description)
774 // TODO Auto-generated method stub
778 public Accessible getAccessibleColumnDescription(int c)
780 // TODO Auto-generated method stub
784 public void setAccessibleColumnDescription(int c, Accessible description)
786 // TODO Auto-generated method stub
790 public boolean isAccessibleSelected(int r, int c)
792 // TODO Auto-generated method stub
796 public boolean isAccessibleRowSelected(int r)
798 // TODO Auto-generated method stub
802 public boolean isAccessibleColumnSelected(int c)
804 // TODO Auto-generated method stub
808 public int[] getSelectedAccessibleRows()
810 // TODO Auto-generated method stub
814 public int[] getSelectedAccessibleColumns()
816 // TODO Auto-generated method stub
823 * The RI returns an instance of such class for table header cells. This
824 * makes sense so I added this class. This still needs to be fully
825 * implemented, I just don't feel motivated enough to do so just now.
827 private class AccessibleJTableHeaderCell
828 extends AccessibleContext
829 implements Accessible, AccessibleComponent
838 * @param h the table header.
841 * @param c the column index.
843 private AccessibleJTableHeaderCell(JTableHeader h, Component comp, int r,
851 * Returns the header renderer.
853 * @return The header renderer.
855 Component getColumnHeaderRenderer()
857 TableColumn tc = header.getColumnModel().getColumn(columnIndex);
858 TableCellRenderer r = tc.getHeaderRenderer();
860 r = header.getDefaultRenderer();
861 return r.getTableCellRendererComponent(header.getTable(),
862 tc.getHeaderValue(), false, false, -1, columnIndex);
866 * Returns the accessible role for the table header cell.
868 * @return The accessible role.
870 public AccessibleRole getAccessibleRole()
872 Component renderer = getColumnHeaderRenderer();
873 if (renderer instanceof Accessible)
875 Accessible ac = (Accessible) renderer;
876 return ac.getAccessibleContext().getAccessibleRole();
881 public AccessibleStateSet getAccessibleStateSet()
883 // TODO Auto-generated method stub
887 public int getAccessibleIndexInParent()
889 // TODO Auto-generated method stub
893 public int getAccessibleChildrenCount()
895 // TODO Auto-generated method stub
899 public Accessible getAccessibleChild(int i)
901 // TODO Auto-generated method stub
905 public Locale getLocale()
907 // TODO Auto-generated method stub
912 * Returns the accessible context.
914 * @return <code>this</code>.
916 public AccessibleContext getAccessibleContext()
921 public Color getBackground()
923 // TODO Auto-generated method stub
927 public void setBackground(Color color)
929 // TODO Auto-generated method stub
933 public Color getForeground()
935 // TODO Auto-generated method stub
939 public void setForeground(Color color)
941 // TODO Auto-generated method stub
945 public Cursor getCursor()
947 // TODO Auto-generated method stub
951 public void setCursor(Cursor cursor)
953 // TODO Auto-generated method stub
957 public Font getFont()
959 // TODO Auto-generated method stub
963 public void setFont(Font font)
965 // TODO Auto-generated method stub
969 public FontMetrics getFontMetrics(Font font)
971 // TODO Auto-generated method stub
975 public boolean isEnabled()
977 // TODO Auto-generated method stub
981 public void setEnabled(boolean b)
983 // TODO Auto-generated method stub
987 public boolean isVisible()
989 // TODO Auto-generated method stub
993 public void setVisible(boolean b)
995 // TODO Auto-generated method stub
999 public boolean isShowing()
1001 // TODO Auto-generated method stub
1005 public boolean contains(Point point)
1007 // TODO Auto-generated method stub
1011 public Point getLocationOnScreen()
1013 // TODO Auto-generated method stub
1017 public Point getLocation()
1019 // TODO Auto-generated method stub
1023 public void setLocation(Point point)
1025 // TODO Auto-generated method stub
1029 public Rectangle getBounds()
1031 // TODO Auto-generated method stub
1035 public void setBounds(Rectangle rectangle)
1037 // TODO Auto-generated method stub
1041 public Dimension getSize()
1043 // TODO Auto-generated method stub
1047 public void setSize(Dimension dimension)
1049 // TODO Auto-generated method stub
1053 public Accessible getAccessibleAt(Point point)
1055 // TODO Auto-generated method stub
1059 public boolean isFocusTraversable()
1061 // TODO Auto-generated method stub
1065 public void requestFocus()
1067 // TODO Auto-generated method stub
1071 public void addFocusListener(FocusListener listener)
1073 // TODO Auto-generated method stub
1077 public void removeFocusListener(FocusListener listener)
1079 // TODO Auto-generated method stub
1086 * The last selected row. This is needed to track the selection in
1087 * {@link #valueChanged(ListSelectionEvent)}.
1089 private int lastSelectedRow;
1092 * The last selected column. This is needed to track the selection in
1093 * {@link #valueChanged(ListSelectionEvent)}.
1095 private int lastSelectedColumn;
1098 * The caption of the table.
1100 private Accessible caption;
1103 * The summary of the table.
1105 private Accessible summary;
1108 * Accessible descriptions for rows.
1110 private Accessible[] rowDescriptions;
1113 * Accessible descriptions for columns.
1115 private Accessible[] columnDescriptions;
1118 * Creates a new <code>AccessibleJTable</code>.
1122 protected AccessibleJTable()
1124 getModel().addTableModelListener(this);
1125 getSelectionModel().addListSelectionListener(this);
1126 getColumnModel().addColumnModelListener(this);
1127 lastSelectedRow = getSelectedRow();
1128 lastSelectedColumn = getSelectedColumn();
1129 TableCellEditor editor = getCellEditor();
1131 editor.addCellEditorListener(this);
1135 * Returns the accessible role for the <code>JTable</code> component.
1137 * @return {@link AccessibleRole#TABLE}.
1139 public AccessibleRole getAccessibleRole()
1141 return AccessibleRole.TABLE;
1145 * Returns the accessible table.
1147 * @return <code>this</code>.
1149 public AccessibleTable getAccessibleTable()
1155 * Returns the number of selected items in this table.
1157 public int getAccessibleSelectionCount()
1159 return getSelectedColumnCount();
1163 * Returns the selected accessible object with the specified index
1164 * <code>i</code>. This basically returns the i-th selected cell in the
1165 * table when going though it row-wise, and inside the rows, column-wise.
1167 * @param i the index of the selected object to find
1169 * @return the selected accessible object with the specified index
1172 public Accessible getAccessibleSelection(int i)
1174 Accessible found = null;
1176 int[] selectedRows = getSelectedRows();
1177 int[] selectedColumns = getSelectedColumns();
1178 int numCols = getColumnCount();
1179 int numRows = getRowCount();
1181 // We have to go through every selected row and column and count until we
1182 // find the specified index. This is potentially inefficient, but I can't
1183 // think of anything better atm.
1184 if (getRowSelectionAllowed() && getColumnSelectionAllowed())
1187 int newIndex = current;
1188 int lastSelectedRow = -1;
1189 // Go through the selected rows array, don't forget the selected
1190 // cells inside the not-selected rows' columns.
1191 for (int j = 0; i < selectedRows.length; i++)
1193 // Handle unselected rows between this selected and the last
1194 // selected row, if any.
1195 int selectedRow = selectedRows[j];
1198 for (r = lastSelectedRow + 1;
1199 r < selectedRow && current < i; r++)
1201 for (ci = 0; ci < selectedColumns.length && current < i;
1209 // We found the cell in the above loops, now get out of here.
1210 found = getAccessibleChild(r * numCols
1211 + selectedColumns[ci]);
1215 // If we're still here, handle the current selected row.
1216 if (current < i && current + numCols >= i)
1218 // The cell must be in that row, which one is it?
1219 found = getAccessibleChild(r * numCols + (i - current));
1226 // The cell can still be in the last couple of unselected rows.
1229 for (r = lastSelectedRow + 1;
1230 r < numRows && current < i; r++)
1232 for (ci = 0; ci < selectedColumns.length && current < i;
1240 // We found the cell in the above loops, now get out of here.
1241 found = getAccessibleChild(r * numCols
1242 + selectedColumns[ci]);
1246 // One or more rows can be completely selected.
1247 else if (getRowSelectionAllowed())
1249 int c = i % numCols;
1250 int r = selectedRows[i / numCols];
1251 found = getAccessibleChild(r * numCols + c);
1253 // One or more columns can be completely selected.
1254 else if (getRowSelectionAllowed())
1256 int numSelectedColumns = selectedColumns.length;
1257 int c = selectedColumns[i % numSelectedColumns];
1258 int r = i / numSelectedColumns;
1259 found = getAccessibleChild(r * numCols + c);
1266 * Returns <code>true</code> if the accessible child with the index
1267 * <code>i</code> is selected, <code>false</code> otherwise.
1269 * @param i the index of the accessible to check
1271 * @return <code>true</code> if the accessible child with the index
1272 * <code>i</code> is selected, <code>false</code> otherwise
1274 public boolean isAccessibleChildSelected(int i)
1276 int r = getAccessibleRowAtIndex(i);
1277 int c = getAccessibleColumnAtIndex(i);
1278 return isCellSelected(r, c);
1282 * Adds the accessible child with the specified index <code>i</code> to the
1285 * @param i the index of the accessible child to add to the selection
1287 public void addAccessibleSelection(int i)
1289 int r = getAccessibleRowAtIndex(i);
1290 int c = getAccessibleColumnAtIndex(i);
1291 changeSelection(r, c, true, false);
1295 * Removes the accessible child with the specified index <code>i</code>
1296 * from the current selection. This will only work on tables that have
1297 * cell selection enabled (<code>rowSelectionAllowed == false &&
1298 * columnSelectionAllowed == false</code>).
1300 * @param i the index of the accessible to be removed from the selection
1302 public void removeAccessibleSelection(int i)
1304 if (! getRowSelectionAllowed() && ! getColumnSelectionAllowed())
1306 int r = getAccessibleRowAtIndex(i);
1307 int c = getAccessibleColumnAtIndex(i);
1308 removeRowSelectionInterval(r, r);
1309 removeColumnSelectionInterval(c, c);
1314 * Deselects all selected accessible children.
1316 public void clearAccessibleSelection()
1322 * Selects all accessible children that can be selected. This will only
1323 * work on tables that support multiple selections and that have individual
1324 * cell selection enabled.
1326 public void selectAllAccessibleSelection()
1332 * Receives notification when the row selection changes and fires
1333 * appropriate property change events.
1335 * @param event the list selection event
1337 public void valueChanged(ListSelectionEvent event)
1339 firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY,
1340 Boolean.FALSE, Boolean.TRUE);
1341 int r = getSelectedRow();
1342 int c = getSelectedColumn();
1343 if (r != lastSelectedRow || c != lastSelectedColumn)
1345 Accessible o = getAccessibleAt(lastSelectedRow,
1346 lastSelectedColumn);
1347 Accessible n = getAccessibleAt(r, c);
1348 firePropertyChange(AccessibleContext
1349 .ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY, o, n);
1350 lastSelectedRow = r;
1351 lastSelectedColumn = c;
1356 * Receives notification when the table model changes. Depending on the
1357 * type of change, this method calls {@link #tableRowsInserted} or
1358 * {@link #tableRowsDeleted}.
1360 * @param event the table model event
1362 public void tableChanged(TableModelEvent event)
1364 switch (event.getType())
1366 case TableModelEvent.INSERT:
1367 tableRowsInserted(event);
1369 case TableModelEvent.DELETE:
1370 tableRowsDeleted(event);
1376 * Receives notification when one or more rows have been inserted into the
1377 * table and fires appropriate property change events.
1379 * @param event the table model event
1381 public void tableRowsInserted(TableModelEvent event)
1383 handleRowChange(event);
1387 * Receives notification when one or more rows have been deleted from the
1390 * @param event the table model event
1392 public void tableRowsDeleted(TableModelEvent event)
1394 handleRowChange(event);
1398 * Fires a PropertyChangeEvent for inserted or deleted rows.
1400 * @param event the table model event
1402 private void handleRowChange(TableModelEvent event)
1404 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
1406 int firstColumn = event.getColumn();
1407 int lastColumn = event.getColumn();
1408 if (firstColumn == TableModelEvent.ALL_COLUMNS)
1411 lastColumn = getColumnCount() - 1;
1413 AccessibleJTableModelChange change = new AccessibleJTableModelChange
1414 (event.getType(), event.getFirstRow(), event.getLastRow(),
1415 firstColumn, lastColumn);
1416 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED,
1420 public void columnAdded(TableColumnModelEvent event)
1422 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
1424 handleColumnChange(AccessibleTableModelChange.INSERT,
1425 event.getFromIndex(), event.getToIndex());
1428 public void columnRemoved(TableColumnModelEvent event)
1430 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
1432 handleColumnChange(AccessibleTableModelChange.DELETE,
1433 event.getFromIndex(), event.getToIndex());
1436 public void columnMoved(TableColumnModelEvent event)
1438 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
1440 handleColumnChange(AccessibleTableModelChange.DELETE,
1441 event.getFromIndex(), event.getFromIndex());
1442 handleColumnChange(AccessibleTableModelChange.INSERT,
1443 event.getFromIndex(), event.getToIndex());
1447 * Fires a PropertyChangeEvent for inserted or deleted columns.
1449 * @param type the type of change
1450 * @param from the start of the change
1451 * @param to the target of the change
1453 private void handleColumnChange(int type, int from, int to)
1455 AccessibleJTableModelChange change =
1456 new AccessibleJTableModelChange(type, 0, 0, from, to);
1457 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED,
1461 public void columnMarginChanged(ChangeEvent event)
1463 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
1467 public void columnSelectionChanged(ListSelectionEvent event)
1469 // AFAICS, nothing is done here.
1472 public void editingCanceled(ChangeEvent event)
1474 // AFAICS, nothing is done here.
1477 public void editingStopped(ChangeEvent event)
1479 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY,
1484 * Receives notification when any of the JTable's properties changes. This
1485 * is used to replace the listeners on the table's model, selection model,
1486 * column model and cell editor.
1488 * @param e the property change event
1490 public void propertyChange(PropertyChangeEvent e)
1492 String propName = e.getPropertyName();
1493 if (propName.equals("tableModel"))
1495 TableModel oldModel = (TableModel) e.getOldValue();
1496 oldModel.removeTableModelListener(this);
1497 TableModel newModel = (TableModel) e.getNewValue();
1498 newModel.addTableModelListener(this);
1500 else if (propName.equals("columnModel"))
1502 TableColumnModel oldModel = (TableColumnModel) e.getOldValue();
1503 oldModel.removeColumnModelListener(this);
1504 TableColumnModel newModel = (TableColumnModel) e.getNewValue();
1505 newModel.addColumnModelListener(this);
1507 else if (propName.equals("selectionModel"))
1509 ListSelectionModel oldModel = (ListSelectionModel) e.getOldValue();
1510 oldModel.removeListSelectionListener(this);
1511 ListSelectionModel newModel = (ListSelectionModel) e.getNewValue();
1512 newModel.addListSelectionListener(this);
1514 else if (propName.equals("cellEditor"))
1516 CellEditor oldEd = (CellEditor) e.getOldValue();
1517 oldEd.removeCellEditorListener(this);
1518 CellEditor newEd = (CellEditor) e.getNewValue();
1519 newEd.addCellEditorListener(this);
1524 * Returns the row number of an accessible child (cell) with the specified
1527 * @param index the index of the cell of which the row number is queried
1529 * @return the row number of an accessible child (cell) with the specified
1532 public int getAccessibleRow(int index)
1534 return getAccessibleRowAtIndex(index);
1538 * Returns the column number of an accessible child (cell) with the
1541 * @param index the index of the cell of which the column number is queried
1543 * @return the column number of an accessible child (cell) with the
1546 public int getAccessibleColumn(int index)
1548 return getAccessibleColumnAtIndex(index);
1552 * Returns the index of the accessible child at the specified row and
1555 * @param r the row number
1556 * @param c the column number
1558 * @return the index of the accessible child at the specified row and
1561 public int getAccessibleIndex(int r, int c)
1563 return getAccessibleIndexAt(r, c);
1567 * Returns the caption of the table.
1569 * @return the caption of the table
1571 * @see #setAccessibleCaption(Accessible)
1573 public Accessible getAccessibleCaption()
1579 * Sets the caption for the table.
1581 * @param c the caption to set
1583 public void setAccessibleCaption(Accessible c)
1589 * Returns the summary for the table.
1591 * @return the summary for the table
1593 public Accessible getAccessibleSummary()
1599 * Sets the summary for the table.
1601 * @param s the summary to set
1603 public void setAccessibleSummary(Accessible s)
1609 * Returns the number of rows in the table.
1611 * @return the number of rows in the table
1613 public int getAccessibleRowCount()
1615 return getRowCount();
1619 * Returns the number of columns in the table.
1621 * @return the number of columns in the table
1623 public int getAccessibleColumnCount()
1625 return getColumnCount();
1629 * Returns the accessible child at the given index.
1631 * @param index the child index.
1633 * @return The accessible child.
1635 public Accessible getAccessibleChild(int index)
1637 int r = getAccessibleRow(index);
1638 int c = getAccessibleColumn(index);
1639 return getAccessibleAt(r, c);
1643 * Returns the accessible child (table cell) at the specified row and
1646 * @param r the row number
1647 * @param c the column number
1649 * @return the accessible child (table cell) at the specified row and
1652 public Accessible getAccessibleAt(int r, int c)
1654 TableCellRenderer cellRenderer = getCellRenderer(r, c);
1655 Component renderer = cellRenderer.getTableCellRendererComponent(
1656 JTable.this, getValueAt(r, c), isCellSelected(r, c), false, r, c);
1657 if (renderer instanceof Accessible)
1658 return (Accessible) renderer;
1663 * Returns the number of rows that the specified cell occupies. The
1664 * standard table cells only occupy one row, so we return <code>1</code>
1667 * @param r the row number
1668 * @param c the column number
1670 * @return the number of rows that the specified cell occupies
1672 public int getAccessibleRowExtentAt(int r, int c)
1678 * Returns the number of columns that the specified cell occupies. The
1679 * standard table cells only occupy one column, so we return <code>1</code>
1682 * @param r the row number
1683 * @param c the column number
1685 * @return the number of rows that the specified cell occupies
1687 public int getAccessibleColumnExtentAt(int r, int c)
1693 * Returns the accessible row header.
1695 * @return the accessible row header
1697 public AccessibleTable getAccessibleRowHeader()
1699 // The RI seems to always return null here, so do we.
1704 * Sets the accessible row header.
1706 * @param header the header to set
1708 public void setAccessibleRowHeader(AccessibleTable header)
1710 // In the RI this seems to be a no-op.
1714 * Returns the column header.
1716 * @return the column header, or <code>null</code> if there is no column
1719 public AccessibleTable getAccessibleColumnHeader()
1721 JTableHeader h = getTableHeader();
1722 AccessibleTable header = null;
1724 header = new AccessibleTableHeader(h);
1729 * Sets the accessible column header. The default implementation doesn't
1730 * allow changing the header this way, so this is a no-op.
1732 * @param header the accessible column header to set
1734 public void setAccessibleColumnHeader(AccessibleTable header)
1736 // The RI doesn't seem to do anything, so we also do nothing.
1740 * Returns the accessible description for the row with the specified index,
1741 * or <code>null</code> if no description has been set.
1743 * @param r the row for which the description is queried
1745 * @return the accessible description for the row with the specified index,
1746 * or <code>null</code> if no description has been set
1748 public Accessible getAccessibleRowDescription(int r)
1750 Accessible descr = null;
1751 if (rowDescriptions != null)
1752 descr = rowDescriptions[r];
1757 * Sets the accessible description for the row with the specified index.
1759 * @param r the row number for which to set the description
1760 * @param description the description to set
1762 public void setAccessibleRowDescription(int r, Accessible description)
1764 if (rowDescriptions == null)
1765 rowDescriptions = new Accessible[getAccessibleRowCount()];
1766 rowDescriptions[r] = description;
1770 * Returns the accessible description for the column with the specified
1771 * index, or <code>null</code> if no description has been set.
1773 * @param c the column for which the description is queried
1775 * @return the accessible description for the column with the specified
1776 * index, or <code>null</code> if no description has been set
1778 public Accessible getAccessibleColumnDescription(int c)
1780 Accessible descr = null;
1781 if (columnDescriptions != null)
1782 descr = columnDescriptions[c];
1787 * Sets the accessible description for the column with the specified index.
1789 * @param c the column number for which to set the description
1790 * @param description the description to set
1792 public void setAccessibleColumnDescription(int c, Accessible description)
1794 if (columnDescriptions == null)
1795 columnDescriptions = new Accessible[getAccessibleRowCount()];
1796 columnDescriptions[c] = description;
1800 * Returns <code>true</code> if the accessible child at the specified
1801 * row and column is selected, <code>false</code> otherwise.
1803 * @param r the row number of the child
1804 * @param c the column number of the child
1806 * @return <code>true</code> if the accessible child at the specified
1807 * row and column is selected, <code>false</code> otherwise
1809 public boolean isAccessibleSelected(int r, int c)
1811 return isCellSelected(r, c);
1815 * Returns <code>true</code> if the row with the specified index is
1816 * selected, <code>false</code> otherwise.
1818 * @param r the row number
1820 * @return <code>true</code> if the row with the specified index is
1821 * selected, <code>false</code> otherwise
1823 public boolean isAccessibleRowSelected(int r)
1825 return isRowSelected(r);
1829 * Returns <code>true</code> if the column with the specified index is
1830 * selected, <code>false</code> otherwise.
1832 * @param c the column number
1834 * @return <code>true</code> if the column with the specified index is
1835 * selected, <code>false</code> otherwise
1837 public boolean isAccessibleColumnSelected(int c)
1839 return isColumnSelected(c);
1843 * Returns the indices of all selected rows.
1845 * @return the indices of all selected rows
1847 public int[] getSelectedAccessibleRows()
1849 return getSelectedRows();
1853 * Returns the indices of all selected columns.
1855 * @return the indices of all selected columns
1857 public int[] getSelectedAccessibleColumns()
1859 return getSelectedColumns();
1863 * Returns the accessible row at the specified index.
1865 * @param index the index for which to query the row
1867 * @return the row number at the specified table index
1869 public int getAccessibleRowAtIndex(int index)
1871 // TODO: Back this up by a Mauve test and update API docs accordingly.
1872 return index / getColumnCount();
1876 * Returns the accessible column at the specified index.
1878 * @param index the index for which to query the column
1880 * @return the column number at the specified table index
1882 public int getAccessibleColumnAtIndex(int index)
1884 // TODO: Back this up by a Mauve test and update API docs accordingly.
1885 return index % getColumnCount();
1889 * Returns the accessible child index at the specified column and row.
1891 * @param row the row
1892 * @param column the column
1894 * @return the index of the accessible child at the specified row and
1897 public int getAccessibleIndexAt(int row, int column)
1899 // TODO: Back this up by a Mauve test and update API docs accordingly.
1900 return row * getColumnCount() + column;
1904 * Handles property changes from the <code>TableColumn</code>s of this
1905 * <code>JTable</code>.
1907 * More specifically, this triggers a {@link #revalidate()} call if the
1908 * preferredWidth of one of the observed columns changes.
1910 class TableColumnPropertyChangeHandler implements PropertyChangeListener
1913 * Receives notification that a property of the observed TableColumns has
1916 * @param ev the property change event
1918 public void propertyChange(PropertyChangeEvent ev)
1920 if (ev.getPropertyName().equals("preferredWidth"))
1922 JTableHeader header = getTableHeader();
1924 // Do nothing if the table is in the resizing mode.
1925 if (header.getResizingColumn() == null)
1927 TableColumn col = (TableColumn) ev.getSource();
1928 header.setResizingColumn(col);
1930 header.setResizingColumn(null);
1937 * A cell renderer for boolean values.
1939 private class BooleanCellRenderer
1940 extends DefaultTableCellRenderer
1943 * The CheckBox that is used for rendering.
1945 private final JCheckBox checkBox;
1948 * Creates a new checkbox based boolean cell renderer. The checkbox is
1949 * centered by default.
1951 BooleanCellRenderer()
1953 checkBox = new JCheckBox();
1954 checkBox.setHorizontalAlignment(SwingConstants.CENTER);
1958 * Get the check box.
1960 JCheckBox getCheckBox()
1966 * Returns the component that is used for rendering the value.
1968 * @param table the JTable
1969 * @param value the value of the object
1970 * @param isSelected is the cell selected?
1971 * @param hasFocus has the cell the focus?
1972 * @param row the row to render
1973 * @param column the cell to render
1974 * @return this component (the default table cell renderer)
1976 public Component getTableCellRendererComponent(JTable table, Object value,
1978 boolean hasFocus, int row,
1983 checkBox.setBackground(table.getSelectionBackground());
1984 checkBox.setForeground(table.getSelectionForeground());
1988 checkBox.setBackground(table.getBackground());
1989 checkBox.setForeground(table.getForeground());
1995 UIManager.getBorder("Table.focusCellHighlightBorder"));
1996 if (table.isCellEditable(row, column))
1998 checkBox.setBackground(
1999 UIManager.getColor("Table.focusCellBackground"));
2000 checkBox.setForeground(
2001 UIManager.getColor("Table.focusCellForeground"));
2005 checkBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
2007 // Null is rendered as false.
2009 checkBox.setSelected(false);
2012 Boolean boolValue = (Boolean) value;
2013 checkBox.setSelected(boolValue.booleanValue());
2020 * A cell renderer for Date values.
2022 private class DateCellRenderer
2023 extends DefaultTableCellRenderer
2026 * Returns the component that is used for rendering the value.
2028 * @param table the JTable
2029 * @param value the value of the object
2030 * @param isSelected is the cell selected?
2031 * @param hasFocus has the cell the focus?
2032 * @param row the row to render
2033 * @param column the cell to render
2035 * @return this component (the default table cell renderer)
2037 public Component getTableCellRendererComponent(JTable table, Object value,
2039 boolean hasFocus, int row,
2042 super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
2044 if (value instanceof Date)
2046 Date dateValue = (Date) value;
2047 DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT);
2048 setText(df.format(dateValue));
2055 * A cell renderer for Double values.
2057 private class DoubleCellRenderer
2058 extends DefaultTableCellRenderer
2061 * Creates a new instance of NumberCellRenderer.
2063 public DoubleCellRenderer()
2065 setHorizontalAlignment(JLabel.RIGHT);
2069 * Returns the component that is used for rendering the value.
2071 * @param table the JTable
2072 * @param value the value of the object
2073 * @param isSelected is the cell selected?
2074 * @param hasFocus has the cell the focus?
2075 * @param row the row to render
2076 * @param column the cell to render
2078 * @return this component (the default table cell renderer)
2080 public Component getTableCellRendererComponent(JTable table, Object value,
2082 boolean hasFocus, int row,
2085 super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
2087 if (value instanceof Double)
2089 Double doubleValue = (Double) value;
2090 NumberFormat nf = NumberFormat.getInstance();
2091 setText(nf.format(doubleValue.doubleValue()));
2098 * A cell renderer for Float values.
2100 private class FloatCellRenderer
2101 extends DefaultTableCellRenderer
2104 * Creates a new instance of NumberCellRenderer.
2106 public FloatCellRenderer()
2108 setHorizontalAlignment(JLabel.RIGHT);
2112 * Returns the component that is used for rendering the value.
2114 * @param table the JTable
2115 * @param value the value of the object
2116 * @param isSelected is the cell selected?
2117 * @param hasFocus has the cell the focus?
2118 * @param row the row to render
2119 * @param column the cell to render
2121 * @return this component (the default table cell renderer)
2123 public Component getTableCellRendererComponent(JTable table, Object value,
2125 boolean hasFocus, int row,
2128 super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
2130 if (value instanceof Float)
2132 Float floatValue = (Float) value;
2133 NumberFormat nf = NumberFormat.getInstance();
2134 setText(nf.format(floatValue.floatValue()));
2141 * A cell renderer for Number values.
2143 private class NumberCellRenderer
2144 extends DefaultTableCellRenderer
2147 * Creates a new instance of NumberCellRenderer.
2149 public NumberCellRenderer()
2151 setHorizontalAlignment(JLabel.RIGHT);
2156 * A cell renderer for Icon values.
2158 private class IconCellRenderer
2159 extends DefaultTableCellRenderer
2163 setHorizontalAlignment(SwingConstants.CENTER);
2168 * Returns the component that is used for rendering the value.
2170 * @param table the JTable
2171 * @param value the value of the object
2172 * @param isSelected is the cell selected?
2173 * @param hasFocus has the cell the focus?
2174 * @param row the row to render
2175 * @param column the cell to render
2177 * @return this component (the default table cell renderer)
2179 public Component getTableCellRendererComponent(JTable table, Object value,
2181 boolean hasFocus, int row,
2184 super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
2186 if (value instanceof Icon)
2188 Icon iconValue = (Icon) value;
2201 * The JTable text component (used in editing) always has the table
2202 * as its parent. The scrollRectToVisible must be adjusted taking the
2203 * relative component position.
2205 * @author Audrius Meskauskas (AudriusA@Bioinformatics.org)
2207 private class TableTextField extends JTextField
2210 * Create the text field without the border.
2214 setBorder(BorderFactory.createLineBorder(getGridColor(), 2));
2219 private static final long serialVersionUID = 3876025080382781659L;
2222 * This table, for referring identically name methods from inner classes.
2224 final JTable this_table = this;
2228 * When resizing columns, do not automatically change any columns. In this
2229 * case the table should be enclosed in a {@link JScrollPane} in order to
2230 * accomodate cases in which the table size exceeds its visible area.
2232 public static final int AUTO_RESIZE_OFF = 0;
2235 * When resizing column <code>i</code>, automatically change only the
2236 * single column <code>i+1</code> to provide or absorb excess space
2239 public static final int AUTO_RESIZE_NEXT_COLUMN = 1;
2242 * When resizing column <code>i</code> in a table of <code>n</code>
2243 * columns, automatically change all columns in the range <code>[i+1,
2244 * n)</code>, uniformly, to provide or absorb excess space requirements.
2246 public static final int AUTO_RESIZE_SUBSEQUENT_COLUMNS = 2;
2249 * When resizing column <code>i</code> in a table of <code>n</code>
2250 * columns, automatically change all columns in the range <code>[0,
2251 * n)</code> (with the exception of column i) uniformly, to provide or
2252 * absorb excess space requirements.
2254 public static final int AUTO_RESIZE_ALL_COLUMNS = 4;
2257 * When resizing column <code>i</code> in a table of <code>n</code>
2258 * columns, automatically change column <code>n-1</code> (the last column
2259 * in the table) to provide or absorb excess space requirements.
2261 public static final int AUTO_RESIZE_LAST_COLUMN = 3;
2264 * A table mapping {@link java.lang.Class} objects to
2265 * {@link TableCellEditor} objects. This table is consulted by the
2268 protected Hashtable defaultEditorsByColumnClass = new Hashtable();
2271 * A table mapping {@link java.lang.Class} objects to
2272 * {@link TableCellEditor} objects. This table is consulted by the
2275 protected Hashtable defaultRenderersByColumnClass = new Hashtable();
2278 * The column that is edited, -1 if the table is not edited currently.
2280 protected int editingColumn;
2283 * The row that is edited, -1 if the table is not edited currently.
2285 protected int editingRow;
2288 * The component that is used for editing.
2289 * <code>null</code> if the table is not editing currently.
2292 protected transient Component editorComp;
2296 * Whether or not the table should automatically compute a matching
2297 * {@link TableColumnModel} and assign it to the {@link #columnModel}
2298 * property when the {@link #dataModel} property is changed.
2300 * @see #setModel(TableModel)
2301 * @see #createDefaultColumnsFromModel()
2302 * @see #setColumnModel(TableColumnModel)
2303 * @see #setAutoCreateColumnsFromModel(boolean)
2304 * @see #getAutoCreateColumnsFromModel()
2306 protected boolean autoCreateColumnsFromModel;
2309 * A numeric code specifying the resizing behavior of the table. Must be
2310 * one of {@link #AUTO_RESIZE_ALL_COLUMNS} (the default), {@link
2311 * #AUTO_RESIZE_LAST_COLUMN}, {@link #AUTO_RESIZE_NEXT_COLUMN}, {@link
2312 * #AUTO_RESIZE_SUBSEQUENT_COLUMNS}, or {@link #AUTO_RESIZE_OFF}.
2315 * @see #setAutoResizeMode(int)
2316 * @see #getAutoResizeMode()
2318 protected int autoResizeMode;
2321 * The height in pixels of any row of the table. All rows in a table are
2322 * of uniform height. This differs from column width, which varies on a
2323 * per-column basis, and is stored in the individual columns of the
2324 * {@link #columnModel}.
2326 * @see #getRowHeight()
2327 * @see #setRowHeight(int)
2328 * @see TableColumn#getWidth()
2329 * @see TableColumn#setWidth(int)
2331 protected int rowHeight;
2334 * The height in pixels of the gap left between any two rows of the table.
2336 * @see #setRowMargin(int)
2337 * @see #getRowHeight()
2338 * @see #getIntercellSpacing()
2339 * @see #setIntercellSpacing(Dimension)
2340 * @see TableColumnModel#getColumnMargin()
2341 * @see TableColumnModel#setColumnMargin(int)
2343 protected int rowMargin;
2346 * Whether or not the table should allow row selection. If the table
2347 * allows both row <em>and</em> column selection, it is said to allow
2348 * "cell selection". Previous versions of the JDK supported cell
2349 * selection as an independent concept, but it is now represented solely
2350 * in terms of simultaneous row and column selection.
2352 * @see TableColumnModel#getColumnSelectionAllowed()
2353 * @see #setRowSelectionAllowed(boolean)
2354 * @see #getRowSelectionAllowed()
2355 * @see #getCellSelectionEnabled()
2356 * @see #setCellSelectionEnabled(boolean)
2358 protected boolean rowSelectionAllowed;
2361 * Obsolete. Use {@link #rowSelectionAllowed}, {@link
2362 * #getColumnSelectionAllowed}, or the combined methods {@link
2363 * #getCellSelectionEnabled} and {@link #setCellSelectionEnabled(boolean)}.
2365 protected boolean cellSelectionEnabled;
2368 * The model for data stored in the table. Confusingly, the published API
2369 * requires that this field be called <code>dataModel</code>, despite its
2370 * property name. The table listens to its model as a {@link
2371 * TableModelListener}.
2373 * @see #tableChanged(TableModelEvent)
2374 * @see TableModel#addTableModelListener(TableModelListener)
2376 protected TableModel dataModel;
2379 * <p>A model of various aspects of the columns of the table, <em>not
2380 * including</em> the data stored in them. The {@link TableColumnModel}
2381 * is principally concerned with holding a set of {@link TableColumn}
2382 * objects, each of which describes the display parameters of a column
2383 * and the numeric index of the column from the data model which the
2384 * column is presenting.</p>
2386 * <p>The TableColumnModel also contains a {@link ListSelectionModel} which
2387 * indicates which columns are currently selected. This selection model
2388 * works in combination with the {@link #selectionModel} of the table
2389 * itself to specify a <em>table selection</em>: a combination of row and
2390 * column selections.</p>
2392 * <p>Most application programmers do not need to work with this property
2393 * at all: setting {@link #autoCreateColumnsFromModel} will construct the
2394 * columnModel automatically, and the table acts as a facade for most of
2395 * the interesting properties of the columnModel anyways.</p>
2397 * @see #setColumnModel(TableColumnModel)
2398 * @see #getColumnModel()
2400 protected TableColumnModel columnModel;
2403 * A model of the rows of this table which are currently selected. This
2404 * model is used in combination with the column selection model held as a
2405 * member of the {@link #columnModel} property, to represent the rows and
2406 * columns (or both: cells) of the table which are currently selected.
2408 * @see #rowSelectionAllowed
2409 * @see #setSelectionModel(ListSelectionModel)
2410 * @see #getSelectionModel()
2411 * @see TableColumnModel#getSelectionModel()
2412 * @see ListSelectionModel#addListSelectionListener(ListSelectionListener)
2414 protected ListSelectionModel selectionModel;
2417 * The current cell editor.
2419 protected TableCellEditor cellEditor;
2422 * Whether or not drag-and-drop is enabled on this table.
2424 * @see #setDragEnabled(boolean)
2425 * @see #getDragEnabled()
2427 private boolean dragEnabled;
2430 * The color to paint the grid lines of the table, when either {@link
2431 * #showHorizontalLines} or {@link #showVerticalLines} is set.
2433 * @see #setGridColor(Color)
2434 * @see #getGridColor()
2436 protected Color gridColor;
2439 * The size this table would prefer its viewport assume, if it is
2440 * contained in a {@link JScrollPane}.
2442 * @see #setPreferredScrollableViewportSize(Dimension)
2443 * @see #getPreferredScrollableViewportSize()
2445 protected Dimension preferredViewportSize;
2448 * The color to paint the background of selected cells. Fires a property
2449 * change event with name {@link #SELECTION_BACKGROUND_CHANGED_PROPERTY}
2450 * when its value changes.
2452 * @see #setSelectionBackground(Color)
2453 * @see #getSelectionBackground()
2455 protected Color selectionBackground;
2458 * The name carried in property change events when the {@link
2459 * #selectionBackground} property changes.
2461 private static final String SELECTION_BACKGROUND_CHANGED_PROPERTY = "selectionBackground";
2464 * The color to paint the foreground of selected cells. Fires a property
2465 * change event with name {@link #SELECTION_FOREGROUND_CHANGED_PROPERTY}
2466 * when its value changes.
2468 * @see #setSelectionForeground(Color)
2469 * @see #getSelectionForeground()
2471 protected Color selectionForeground;
2474 * The name carried in property change events when the
2475 * {@link #selectionForeground} property changes.
2477 private static final String SELECTION_FOREGROUND_CHANGED_PROPERTY = "selectionForeground";
2480 * The showHorizontalLines property.
2482 protected boolean showHorizontalLines;
2485 * The showVerticalLines property.
2487 protected boolean showVerticalLines;
2490 * The tableHeader property.
2492 protected JTableHeader tableHeader;
2495 * The property handler for this table's columns.
2497 TableColumnPropertyChangeHandler tableColumnPropertyChangeHandler =
2498 new TableColumnPropertyChangeHandler();
2501 * Whether cell editors should receive keyboard focus when the table is
2504 private boolean surrendersFocusOnKeystroke = false;
2507 * A Rectangle object to be reused in {@link #getCellRect}.
2509 private Rectangle rectCache = new Rectangle();
2512 * Indicates if the rowHeight property has been set by a client program or by
2515 * @see #setUIProperty(String, Object)
2516 * @see LookAndFeel#installProperty(JComponent, String, Object)
2518 private boolean clientRowHeightSet = false;
2521 * Stores the sizes and positions of each row, when using non-uniform row
2522 * heights. Initially the height of all rows is equal and stored in
2523 * {link #rowHeight}. However, when an application calls
2524 * {@link #setRowHeight(int,int)}, the table switches to non-uniform
2525 * row height mode which stores the row heights in the SizeSequence
2528 * @see #setRowHeight(int)
2529 * @see #getRowHeight()
2530 * @see #getRowHeight(int)
2531 * @see #setRowHeight(int, int)
2533 private SizeSequence rowHeights;
2536 * This editor serves just a marker that the value must be simply changed to
2537 * the opposite one instead of starting the editing session.
2539 private transient TableCellEditor booleanInvertingEditor;
2542 * Creates a new <code>JTable</code> instance.
2546 this(null, null, null);
2550 * Creates a new <code>JTable</code> instance with the given number
2551 * of rows and columns.
2553 * @param numRows an <code>int</code> value
2554 * @param numColumns an <code>int</code> value
2556 public JTable (int numRows, int numColumns)
2558 this(new DefaultTableModel(numRows, numColumns));
2562 * Creates a new <code>JTable</code> instance, storing the given data
2563 * array and heaving the given column names. To see the column names,
2564 * you must place the JTable into the {@link JScrollPane}.
2566 * @param data an <code>Object[][]</code> the table data
2567 * @param columnNames an <code>Object[]</code> the column headers
2569 public JTable(Object[][] data, Object[] columnNames)
2571 this(new DefaultTableModel(data, columnNames));
2575 * Creates a new <code>JTable</code> instance, using the given data model
2576 * object that provides information about the table content. The table model
2577 * object is asked for the table size, other features and also receives
2578 * notifications in the case when the table has been edited by the user.
2583 public JTable (TableModel model)
2585 this(model, null, null);
2589 * Creates a new <code>JTable</code> instance, using the given model object
2590 * that provides information about the table content. The table data model
2591 * object is asked for the table size, other features and also receives
2592 * notifications in the case when the table has been edited by the user. The
2593 * table column model provides more detailed control on the table column
2597 * the table data mode
2599 * the table column model
2601 public JTable (TableModel dm, TableColumnModel cm)
2607 * Creates a new <code>JTable</code> instance, providing data model,
2608 * column model and list selection model. The list selection model
2609 * manages the selections.
2611 * @param dm data model (manages table data)
2612 * @param cm column model (manages table columns)
2613 * @param sm list selection model (manages table selections)
2615 public JTable (TableModel dm, TableColumnModel cm, ListSelectionModel sm)
2617 boolean autoCreate = false;
2618 TableColumnModel columnModel;
2623 columnModel = createDefaultColumnModel();
2627 // Initialise the intercelar spacing before setting the column model to
2628 // avoid firing unnecessary events.
2629 // The initial incellar spacing is new Dimenstion(1,1).
2631 columnModel.setColumnMargin(1);
2632 setColumnModel(columnModel);
2634 setSelectionModel(sm == null ? createDefaultSelectionModel() : sm);
2635 setModel(dm == null ? createDefaultDataModel() : dm);
2636 setAutoCreateColumnsFromModel(autoCreate);
2637 initializeLocalVars();
2639 // The following four lines properly set the lead selection indices.
2640 // After this, the UI will handle the lead selection indices.
2641 // FIXME: this should probably not be necessary, if the UI is installed
2642 // before the TableModel is set then the UI will handle things on its
2643 // own, but certain variables need to be set before the UI can be installed
2644 // so we must get the correct order for all the method calls in this
2646 // These four lines are not needed. A Mauve test that shows this is
2647 // gnu.testlet.javax.swing.JTable.constructors(linesNotNeeded).
2648 // selectionModel.setAnchorSelectionIndex(-1);
2649 // selectionModel.setLeadSelectionIndex(-1);
2650 // columnModel.getSelectionModel().setAnchorSelectionIndex(-1);
2651 // columnModel.getSelectionModel().setLeadSelectionIndex(-1);
2656 * Creates a new <code>JTable</code> instance that uses data and column
2657 * names, stored in {@link Vector}s.
2659 * @param data the table data
2660 * @param columnNames the table column names.
2662 public JTable(Vector data, Vector columnNames)
2664 this(new DefaultTableModel(data, columnNames));
2668 * Initialize local variables to default values.
2670 protected void initializeLocalVars()
2672 setTableHeader(createDefaultTableHeader());
2673 if (autoCreateColumnsFromModel)
2674 createDefaultColumnsFromModel();
2675 this.columnModel.addColumnModelListener(this);
2677 this.autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS;
2680 this.rowSelectionAllowed = true;
2682 // this.accessibleContext = new AccessibleJTable();
2683 this.cellEditor = null;
2685 // COMPAT: Both Sun and IBM have drag enabled
2686 this.dragEnabled = false;
2687 this.preferredViewportSize = new Dimension(450,400);
2688 this.showHorizontalLines = true;
2689 this.showVerticalLines = true;
2690 this.editingColumn = -1;
2691 this.editingRow = -1;
2695 * Add the new table column. The table column class allows to specify column
2696 * features more precisely, setting the preferred width, column data type
2697 * (column class) and table headers.
2699 * There is no need the add columns to the table if the default column
2700 * handling is sufficient.
2703 * the new column to add.
2705 public void addColumn(TableColumn column)
2707 if (column.getHeaderValue() == null)
2709 String name = dataModel.getColumnName(column.getModelIndex());
2710 column.setHeaderValue(name);
2713 columnModel.addColumn(column);
2714 column.addPropertyChangeListener(tableColumnPropertyChangeHandler);
2718 * Create the default editors for this table. The default method creates
2719 * the editor for Booleans.
2721 * Other fields are edited as strings at the moment.
2723 protected void createDefaultEditors()
2725 JCheckBox box = new BooleanCellRenderer().getCheckBox();
2726 box.setBorder(BorderFactory.createLineBorder(getGridColor(), 2));
2727 box.setBorderPainted(true);
2728 booleanInvertingEditor = new DefaultCellEditor(box);
2729 setDefaultEditor(Boolean.class, booleanInvertingEditor);
2733 * Create the default renderers for this table. The default method creates
2734 * renderers for Boolean, Number, Double, Date, Icon and ImageIcon.
2737 protected void createDefaultRenderers()
2739 setDefaultRenderer(Boolean.class, new BooleanCellRenderer());
2740 setDefaultRenderer(Number.class, new NumberCellRenderer());
2741 setDefaultRenderer(Double.class, new DoubleCellRenderer());
2742 setDefaultRenderer(Double.class, new FloatCellRenderer());
2743 setDefaultRenderer(Date.class, new DateCellRenderer());
2744 setDefaultRenderer(Icon.class, new IconCellRenderer());
2745 setDefaultRenderer(ImageIcon.class, new IconCellRenderer());
2749 * @deprecated 1.0.2, replaced by <code>new JScrollPane(JTable)</code>
2751 public static JScrollPane createScrollPaneForTable(JTable table)
2753 return new JScrollPane(table);
2757 * Create the default table column model that is used if the user-defined
2758 * column model is not provided. The default method creates
2759 * {@link DefaultTableColumnModel}.
2761 * @return the created table column model.
2763 protected TableColumnModel createDefaultColumnModel()
2765 return new DefaultTableColumnModel();
2769 * Create the default table data model that is used if the user-defined
2770 * data model is not provided. The default method creates
2771 * {@link DefaultTableModel}.
2773 * @return the created table data model.
2775 protected TableModel createDefaultDataModel()
2777 return new DefaultTableModel();
2781 * Create the default table selection model that is used if the user-defined
2782 * selection model is not provided. The default method creates
2783 * {@link DefaultListSelectionModel}.
2785 * @return the created table data model.
2787 protected ListSelectionModel createDefaultSelectionModel()
2789 return new DefaultListSelectionModel();
2793 * Create the default table header, if the user - defined table header is not
2796 * @return the default table header.
2798 protected JTableHeader createDefaultTableHeader()
2800 return new JTableHeader(columnModel);
2804 * Invoked when the column is added. Revalidates and repains the table.
2806 public void columnAdded (TableColumnModelEvent event)
2813 * Invoked when the column margin is changed.
2814 * Revalidates and repains the table.
2816 public void columnMarginChanged (ChangeEvent event)
2823 * Invoked when the column is moved. Revalidates and repains the table.
2825 public void columnMoved (TableColumnModelEvent event)
2828 editingCanceled(null);
2834 * Invoked when the column is removed. Revalidates and repains the table.
2836 public void columnRemoved (TableColumnModelEvent event)
2843 * Invoked when the the column selection changes, repaints the changed
2844 * columns. It is not recommended to override this method, register the
2847 public void columnSelectionChanged (ListSelectionEvent event)
2849 // We must limit the indices to the bounds of the JTable's model, because
2850 // we might get values of -1 or greater then columnCount in the case
2851 // when columns get removed.
2852 int idx0 = Math.max(0, Math.min(getColumnCount() - 1,
2853 event.getFirstIndex()));
2854 int idxn = Math.max(0, Math.min(getColumnCount() - 1,
2855 event.getLastIndex()));
2858 int maxRow = getRowCount() - 1;
2859 if (getRowSelectionAllowed())
2861 minRow = selectionModel.getMinSelectionIndex();
2862 maxRow = selectionModel.getMaxSelectionIndex();
2863 int leadRow = selectionModel.getLeadSelectionIndex();
2864 if (minRow == -1 && maxRow == -1)
2871 // In this case we need to repaint also the range to leadRow, not
2872 // only between min and max.
2875 minRow = Math.min(minRow, leadRow);
2876 maxRow = Math.max(maxRow, leadRow);
2880 if (minRow != -1 && maxRow != -1)
2882 Rectangle first = getCellRect(minRow, idx0, false);
2883 Rectangle last = getCellRect(maxRow, idxn, false);
2884 Rectangle dirty = SwingUtilities.computeUnion(first.x, first.y,
2886 first.height, last);
2892 * Invoked when the editing is cancelled.
2894 public void editingCanceled (ChangeEvent event)
2896 if (editorComp!=null)
2899 repaint(editorComp.getBounds());
2905 * Finish the current editing session and update the table with the
2906 * new value by calling {@link #setValueAt}.
2908 * @param event the change event
2910 public void editingStopped (ChangeEvent event)
2912 if (editorComp!=null)
2915 setValueAt(cellEditor.getCellEditorValue(), editingRow, editingColumn);
2916 repaint(editorComp.getBounds());
2919 requestFocusInWindow();
2923 * Invoked when the table changes.
2924 * <code>null</code> means everything changed.
2926 public void tableChanged (TableModelEvent event)
2928 // update the column model from the table model if the structure has
2929 // changed and the flag autoCreateColumnsFromModel is set
2930 if (event == null || (event.getFirstRow() == TableModelEvent.HEADER_ROW))
2931 handleCompleteChange(event);
2932 else if (event.getType() == TableModelEvent.INSERT)
2933 handleInsert(event);
2934 else if (event.getType() == TableModelEvent.DELETE)
2935 handleDelete(event);
2937 handleUpdate(event);
2941 * Handles a request for complete relayout. This is the case when
2942 * event.getFirstRow() == TableModelEvent.HEADER_ROW.
2944 * @param ev the table model event
2946 private void handleCompleteChange(TableModelEvent ev)
2951 if (getAutoCreateColumnsFromModel())
2952 createDefaultColumnsFromModel();
2958 * Handles table model insertions.
2960 * @param ev the table model event
2962 private void handleInsert(TableModelEvent ev)
2964 // Sync selection model with data model.
2965 int first = ev.getFirstRow();
2968 int last = ev.getLastRow();
2970 last = getRowCount() - 1;
2971 selectionModel.insertIndexInterval(first, last - first + 1, true);
2974 // For variable height rows we must update the SizeSequence thing.
2975 if (rowHeights != null)
2977 rowHeights.insertEntries(first, last - first + 1, rowHeight);
2978 // TODO: We repaint the whole thing when the rows have variable
2979 // heights. We might want to handle this better though.
2984 // Repaint the dirty region and revalidate.
2985 int rowHeight = getRowHeight();
2986 Rectangle dirty = new Rectangle(0, first * rowHeight,
2987 getColumnModel().getTotalColumnWidth(),
2988 (getRowCount() - first) * rowHeight);
2995 * Handles table model deletions.
2997 * @param ev the table model event
2999 private void handleDelete(TableModelEvent ev)
3001 // Sync selection model with data model.
3002 int first = ev.getFirstRow();
3005 int last = ev.getLastRow();
3007 last = getRowCount() - 1;
3009 selectionModel.removeIndexInterval(first, last);
3013 if (dataModel.getRowCount() == 0)
3016 // For variable height rows we must update the SizeSequence thing.
3017 if (rowHeights != null)
3019 rowHeights.removeEntries(first, last - first + 1);
3020 // TODO: We repaint the whole thing when the rows have variable
3021 // heights. We might want to handle this better though.
3026 // Repaint the dirty region and revalidate.
3027 int rowHeight = getRowHeight();
3028 int oldRowCount = getRowCount() + last - first + 1;
3029 Rectangle dirty = new Rectangle(0, first * rowHeight,
3030 getColumnModel().getTotalColumnWidth(),
3031 (oldRowCount - first) * rowHeight);
3038 * Handles table model updates without structural changes.
3040 * @param ev the table model event
3042 private void handleUpdate(TableModelEvent ev)
3044 if (rowHeights == null)
3046 // Some cells have been changed without changing the structure.
3047 // Figure out the dirty rectangle and repaint.
3048 int firstRow = ev.getFirstRow();
3049 int lastRow = ev.getLastRow();
3050 int col = ev.getColumn();
3052 if (col == TableModelEvent.ALL_COLUMNS)
3054 // All columns changed.
3055 dirty = new Rectangle(0, firstRow * getRowHeight(),
3056 getColumnModel().getTotalColumnWidth(), 0);
3060 // Only one cell or column of cells changed.
3061 // We need to convert to view column first.
3062 int column = convertColumnIndexToModel(col);
3063 dirty = getCellRect(firstRow, column, false);
3066 // Now adjust the height of the dirty region.
3067 dirty.height = (lastRow + 1) * getRowHeight();
3073 // TODO: We repaint the whole thing when the rows have variable
3074 // heights. We might want to handle this better though.
3080 * Helper method for adjusting the lead and anchor indices when the
3081 * table structure changed. This sets the lead and anchor to -1 if there's
3082 * no more rows, or set them to 0 when they were at -1 and there are actually
3085 private void checkSelection()
3087 TableModel m = getModel();
3088 ListSelectionModel sm = selectionModel;
3091 int lead = sm.getLeadSelectionIndex();
3092 int c = m.getRowCount();
3093 if (c == 0 && lead != -1)
3095 // No rows in the model, reset lead and anchor to -1.
3096 sm.setValueIsAdjusting(true);
3097 sm.setAnchorSelectionIndex(-1);
3098 sm.setLeadSelectionIndex(-1);
3099 sm.setValueIsAdjusting(false);
3101 else if (c != 0 && lead == -1)
3103 // We have rows, but no lead/anchor. Set them to 0. We
3104 // do a little trick here so that the actual selection is not
3106 if (sm.isSelectedIndex(0))
3107 sm.addSelectionInterval(0, 0);
3109 sm.removeSelectionInterval(0, 0);
3111 // Nothing to do in the other cases.
3116 * Invoked when another table row is selected. It is not recommended
3117 * to override thid method, register the listener instead.
3119 public void valueChanged (ListSelectionEvent event)
3121 // If we are in the editing process, end the editing session.
3123 editingStopped(null);
3125 // Repaint the changed region.
3126 int first = Math.max(0, Math.min(getRowCount() - 1, event.getFirstIndex()));
3127 int last = Math.max(0, Math.min(getRowCount() - 1, event.getLastIndex()));
3128 Rectangle rect1 = getCellRect(first, 0, false);
3129 Rectangle rect2 = getCellRect(last, getColumnCount() - 1, false);
3130 Rectangle dirty = SwingUtilities.computeUnion(rect2.x, rect2.y,
3131 rect2.width, rect2.height,
3137 * Returns index of the column that contains specified point
3138 * or -1 if this table doesn't contain this point.
3140 * @param point point to identify the column
3141 * @return index of the column that contains specified point or
3142 * -1 if this table doesn't contain this point.
3144 public int columnAtPoint(Point point)
3146 int ncols = getColumnCount();
3147 Dimension gap = getIntercellSpacing();
3148 TableColumnModel cols = getColumnModel();
3151 for (int i = 0; i < ncols; ++i)
3153 int width = cols.getColumn(i).getWidth()
3154 + (gap == null ? 0 : gap.width);
3155 if (0 <= x && x < width)
3163 * Returns index of the row that contains specified point or -1 if this table
3164 * doesn't contain this point.
3166 * @param point point to identify the row
3167 * @return index of the row that contains specified point or -1 if this table
3168 * doesn't contain this point.
3170 public int rowAtPoint(Point point)
3174 int nrows = getRowCount();
3177 if (rowHeights == null)
3179 int height = getRowHeight();
3183 r = rowHeights.getIndex(y);
3185 if (r < 0 || r >= nrows)
3195 * Calculate the visible rectangle for a particular row and column. The
3196 * row and column are specified in visual terms; the column may not match
3197 * the {@link #dataModel} column.
3199 * @param row the visible row to get the cell rectangle of
3201 * @param column the visible column to get the cell rectangle of, which may
3202 * differ from the {@link #dataModel} column
3204 * @param includeSpacing whether or not to include the cell margins in the
3205 * resulting cell. If <code>false</code>, the result will only contain the
3206 * inner area of the target cell, not including its margins.
3208 * @return a rectangle enclosing the specified cell
3210 public Rectangle getCellRect(int row,
3212 boolean includeSpacing)
3214 Rectangle cellRect = new Rectangle(0, 0, 0, 0);
3216 // Check for valid range vertically.
3217 if (row >= getRowCount())
3219 cellRect.height = getHeight();
3223 cellRect.height = getRowHeight(row);
3224 if (rowHeights == null)
3225 cellRect.y = row * cellRect.height;
3227 cellRect.y = rowHeights.getPosition(row);
3229 if (! includeSpacing)
3231 // The rounding here is important.
3232 int rMargin = getRowMargin();
3233 cellRect.y += rMargin / 2;
3234 cellRect.height -= rMargin;
3237 // else row < 0, y = height = 0
3239 // Check for valid range horizontally.
3242 if (! getComponentOrientation().isLeftToRight())
3244 cellRect.x = getWidth();
3247 else if (column >= getColumnCount())
3249 if (getComponentOrientation().isLeftToRight())
3251 cellRect.x = getWidth();
3256 TableColumnModel tcm = getColumnModel();
3257 if (getComponentOrientation().isLeftToRight())
3259 for (int i = 0; i < column; i++)
3260 cellRect.x += tcm.getColumn(i).getWidth();
3264 for (int i = tcm.getColumnCount() - 1; i > column; i--)
3265 cellRect.x += tcm.getColumn(i).getWidth();
3267 cellRect.width = tcm.getColumn(column).getWidth();
3268 if (! includeSpacing)
3270 // The rounding here is important.
3271 int cMargin = tcm.getColumnMargin();
3272 cellRect.x += cMargin / 2;
3273 cellRect.width -= cMargin;
3280 public void clearSelection()
3282 selectionModel.clearSelection();
3283 getColumnModel().getSelectionModel().clearSelection();
3287 * Get the value of the selectedRow property by delegation to
3288 * the {@link ListSelectionModel#getMinSelectionIndex} method of the
3289 * {@link #selectionModel} field.
3291 * @return The current value of the selectedRow property
3293 public int getSelectedRow ()
3295 return selectionModel.getMinSelectionIndex();
3299 * Get the value of the {@link #selectionModel} property.
3301 * @return The current value of the property
3303 public ListSelectionModel getSelectionModel()
3305 //Neither Sun nor IBM returns null if rowSelection not allowed
3306 return selectionModel;
3309 public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)
3312 if (orientation == SwingConstants.HORIZONTAL)
3314 block = visibleRect.width;
3318 int rowHeight = getRowHeight();
3320 block = Math.max(rowHeight, // Little hack for useful rounding.
3321 (visibleRect.height / rowHeight) * rowHeight);
3323 block = visibleRect.height;
3329 * Get the value of the <code>scrollableTracksViewportHeight</code> property.
3331 * @return The constant value <code>false</code>
3333 public boolean getScrollableTracksViewportHeight()
3339 * Get the value of the <code>scrollableTracksViewportWidth</code> property.
3341 * @return <code>true</code> unless the {@link #autoResizeMode} property is
3342 * <code>AUTO_RESIZE_OFF</code>
3344 public boolean getScrollableTracksViewportWidth()
3346 if (autoResizeMode == AUTO_RESIZE_OFF)
3353 * Return the preferred scrolling amount (in pixels) for the given scrolling
3354 * direction and orientation. This method handles a partially exposed row by
3355 * returning the distance required to completely expose the item. When
3356 * scrolling the top item is completely exposed.
3358 * @param visibleRect the currently visible part of the component.
3359 * @param orientation the scrolling orientation
3360 * @param direction the scrolling direction (negative - up, positive -down).
3361 * The values greater than one means that more mouse wheel or similar
3362 * events were generated, and hence it is better to scroll the longer
3365 * @author Roman Kennke (kennke@aicas.com)
3367 public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation,
3371 if (orientation == SwingConstants.HORIZONTAL)
3375 unit = getRowHeight();
3376 // The following adjustment doesn't work for variable height rows.
3377 // It fully exposes partially visible rows in the scrolling direction.
3378 if (rowHeights == null)
3383 // How much pixles are exposed from the last item?
3384 int exposed = (visibleRect.y + visibleRect.height) % unit;
3385 if (exposed > 0 && exposed < unit - 1)
3386 unit = unit - exposed - 1;
3391 int exposed = visibleRect.y % unit;
3392 if (exposed > 0 && exposed < unit)
3401 * Get the cell editor, suitable for editing the given cell. The default
3402 * method requests the editor from the column model. If the column model does
3403 * not provide the editor, the call is forwarded to the
3404 * {@link #getDefaultEditor(Class)} with the parameter, obtained from
3405 * {@link TableModel#getColumnClass(int)}.
3407 * @param row the cell row
3408 * @param column the cell column
3409 * @return the editor to edit that cell
3411 public TableCellEditor getCellEditor(int row, int column)
3413 TableCellEditor editor = columnModel.getColumn(column).getCellEditor();
3417 int mcolumn = convertColumnIndexToModel(column);
3418 editor = getDefaultEditor(dataModel.getColumnClass(mcolumn));
3425 * Get the default editor for editing values of the given type
3426 * (String, Boolean and so on).
3428 * @param columnClass the class of the value that will be edited.
3430 * @return the editor, suitable for editing this data type
3432 public TableCellEditor getDefaultEditor(Class<?> columnClass)
3434 if (defaultEditorsByColumnClass.containsKey(columnClass))
3435 return (TableCellEditor) defaultEditorsByColumnClass.get(columnClass);
3438 JTextField t = new TableTextField();
3439 TableCellEditor r = new DefaultCellEditor(t);
3440 defaultEditorsByColumnClass.put(columnClass, r);
3446 * Get the cell renderer for rendering the given cell.
3448 * @param row the cell row
3449 * @param column the cell column
3450 * @return the cell renderer to render that cell.
3452 public TableCellRenderer getCellRenderer(int row, int column)
3454 TableCellRenderer renderer = null;
3455 if (columnModel.getColumnCount() > 0)
3456 renderer = columnModel.getColumn(column).getCellRenderer();
3457 if (renderer == null)
3459 int mcolumn = convertColumnIndexToModel(column);
3460 renderer = getDefaultRenderer(dataModel.getColumnClass(mcolumn));
3466 * Set default renderer for rendering the given data type.
3468 * @param columnClass the data type (String, Boolean and so on) that must be
3470 * @param rend the renderer that will rend this data type
3472 public void setDefaultRenderer(Class<?> columnClass, TableCellRenderer rend)
3474 defaultRenderersByColumnClass.put(columnClass, rend);
3478 * Get the default renderer for rendering the given data type.
3480 * @param columnClass the data that must be rendered
3482 * @return the appropriate defauld renderer for rendering that data type.
3484 public TableCellRenderer getDefaultRenderer(Class<?> columnClass)
3486 if (defaultRenderersByColumnClass.containsKey(columnClass))
3487 return (TableCellRenderer) defaultRenderersByColumnClass.get(columnClass);
3490 TableCellRenderer r = new DefaultTableCellRenderer();
3491 defaultRenderersByColumnClass.put(columnClass, r);
3497 * Convert the table model index into the table column number.
3498 * The model number need not match the real column position. The columns
3499 * may be rearranged by the user with mouse at any time by dragging the
3502 * @param vc the column number (0=first).
3504 * @return the table column model index of this column.
3506 * @see TableColumn#getModelIndex()
3508 public int convertColumnIndexToModel(int vc)
3513 return columnModel.getColumn(vc).getModelIndex();
3517 * Convert the table column number to the table column model index.
3518 * The model number need not match the real column position. The columns
3519 * may be rearranged by the user with mouse at any time by dragging the
3522 * @param mc the table column index (0=first).
3524 * @return the table column number in the model
3526 * @see TableColumn#getModelIndex()
3528 public int convertColumnIndexToView(int mc)
3532 int ncols = getColumnCount();
3533 for (int vc = 0; vc < ncols; ++vc)
3535 if (columnModel.getColumn(vc).getModelIndex() == mc)
3542 * Prepare the renderer for rendering the given cell.
3544 * @param renderer the renderer being prepared
3545 * @param row the row of the cell being rendered
3546 * @param column the column of the cell being rendered
3548 * @return the component which .paint() method will paint the cell.
3550 public Component prepareRenderer(TableCellRenderer renderer,
3554 boolean rowSelAllowed = getRowSelectionAllowed();
3555 boolean colSelAllowed = getColumnSelectionAllowed();
3556 boolean isSel = false;
3557 if (rowSelAllowed && colSelAllowed || !rowSelAllowed && !colSelAllowed)
3558 isSel = isCellSelected(row, column);
3560 isSel = isRowSelected(row) && getRowSelectionAllowed()
3561 || isColumnSelected(column) && getColumnSelectionAllowed();
3563 // Determine the focused cell. The focused cell is the cell at the
3564 // leadSelectionIndices of the row and column selection model.
3565 ListSelectionModel rowSel = getSelectionModel();
3566 ListSelectionModel colSel = getColumnModel().getSelectionModel();
3567 boolean hasFocus = hasFocus() && isEnabled()
3568 && rowSel.getLeadSelectionIndex() == row
3569 && colSel.getLeadSelectionIndex() == column;
3571 return renderer.getTableCellRendererComponent(this,
3572 dataModel.getValueAt(row,
3573 convertColumnIndexToModel(column)),
3581 * Get the value of the {@link #autoCreateColumnsFromModel} property.
3583 * @return The current value of the property
3585 public boolean getAutoCreateColumnsFromModel()
3587 return autoCreateColumnsFromModel;
3591 * Get the value of the {@link #autoResizeMode} property.
3593 * @return The current value of the property
3595 public int getAutoResizeMode()
3597 return autoResizeMode;
3601 * Get the value of the {@link #rowHeight} property.
3603 * @return The current value of the property
3605 public int getRowHeight()
3611 * Get the height of the specified row.
3613 * @param row the row whose height to return
3615 public int getRowHeight(int row)
3618 if (rowHeights != null)
3619 rh = rowHeights.getSize(row);
3625 * Get the value of the {@link #rowMargin} property.
3627 * @return The current value of the property
3629 public int getRowMargin()
3635 * Get the value of the {@link #rowSelectionAllowed} property.
3637 * @return The current value of the property
3639 * @see #setRowSelectionAllowed(boolean)
3641 public boolean getRowSelectionAllowed()
3643 return rowSelectionAllowed;
3647 * Get the value of the {@link #cellSelectionEnabled} property.
3649 * @return The current value of the property
3651 public boolean getCellSelectionEnabled()
3653 return getColumnSelectionAllowed() && getRowSelectionAllowed();
3657 * Get the value of the {@link #dataModel} property.
3659 * @return The current value of the property
3661 public TableModel getModel()
3667 * Get the value of the <code>columnCount</code> property by
3668 * delegation to the {@link #columnModel} field.
3670 * @return The current value of the columnCount property
3672 public int getColumnCount()
3674 return columnModel.getColumnCount();
3678 * Get the value of the <code>rowCount</code> property by
3679 * delegation to the {@link #dataModel} field.
3681 * @return The current value of the rowCount property
3683 public int getRowCount()
3685 return dataModel.getRowCount();
3689 * Get the value of the {@link #columnModel} property.
3691 * @return The current value of the property
3693 public TableColumnModel getColumnModel()
3699 * Get the value of the <code>selectedColumn</code> property by
3700 * delegation to the {@link #columnModel} field.
3702 * @return The current value of the selectedColumn property
3704 public int getSelectedColumn()
3706 return columnModel.getSelectionModel().getMinSelectionIndex();
3709 private static int countSelections(ListSelectionModel lsm)
3711 int lo = lsm.getMinSelectionIndex();
3712 int hi = lsm.getMaxSelectionIndex();
3714 if (lo != -1 && hi != -1)
3716 switch (lsm.getSelectionMode())
3718 case ListSelectionModel.SINGLE_SELECTION:
3722 case ListSelectionModel.SINGLE_INTERVAL_SELECTION:
3726 case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION:
3727 for (int i = lo; i <= hi; ++i)
3728 if (lsm.isSelectedIndex(i))
3736 private static int[] getSelections(ListSelectionModel lsm)
3738 int sz = countSelections(lsm);
3739 int [] ret = new int[sz];
3741 int lo = lsm.getMinSelectionIndex();
3742 int hi = lsm.getMaxSelectionIndex();
3744 if (lo != -1 && hi != -1)
3746 switch (lsm.getSelectionMode())
3748 case ListSelectionModel.SINGLE_SELECTION:
3752 case ListSelectionModel.SINGLE_INTERVAL_SELECTION:
3753 for (int i = lo; i <= hi; ++i)
3757 case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION:
3758 for (int i = lo; i <= hi; ++i)
3759 if (lsm.isSelectedIndex(i))
3768 * Get the value of the <code>selectedColumnCount</code> property by
3769 * delegation to the {@link #columnModel} field.
3771 * @return The current value of the selectedColumnCount property
3773 public int getSelectedColumnCount()
3775 return countSelections(columnModel.getSelectionModel());
3779 * Get the value of the <code>selectedColumns</code> property by
3780 * delegation to the {@link #columnModel} field.
3782 * @return The current value of the selectedColumns property
3784 public int[] getSelectedColumns()
3786 return getSelections(columnModel.getSelectionModel());
3790 * Get the value of the <code>columnSelectionAllowed</code> property.
3792 * @return The current value of the columnSelectionAllowed property
3794 * @see #setColumnSelectionAllowed(boolean)
3796 public boolean getColumnSelectionAllowed()
3798 return getColumnModel().getColumnSelectionAllowed();
3802 * Get the value of the <code>selectedRowCount</code> property by
3803 * delegation to the {@link #selectionModel} field.
3805 * @return The current value of the selectedRowCount property
3807 public int getSelectedRowCount()
3809 return countSelections(selectionModel);
3813 * Get the value of the <code>selectedRows</code> property by
3814 * delegation to the {@link #selectionModel} field.
3816 * @return The current value of the selectedRows property
3818 public int[] getSelectedRows()
3820 return getSelections(selectionModel);
3824 * Get the value of the {@link #accessibleContext} property.
3826 * @return The current value of the property
3828 public AccessibleContext getAccessibleContext()
3830 if (accessibleContext == null)
3832 AccessibleJTable ctx = new AccessibleJTable();
3833 addPropertyChangeListener(ctx);
3834 TableColumnModel tcm = getColumnModel();
3835 tcm.addColumnModelListener(ctx);
3836 tcm.getSelectionModel().addListSelectionListener(ctx);
3837 getSelectionModel().addListSelectionListener(ctx);
3839 accessibleContext = ctx;
3841 return accessibleContext;
3845 * Get the value of the {@link #cellEditor} property.
3847 * @return The current value of the property
3849 public TableCellEditor getCellEditor()
3855 * Get the value of the {@link #dragEnabled} property.
3857 * @return The current value of the property
3859 public boolean getDragEnabled()
3865 * Get the value of the {@link #gridColor} property.
3867 * @return The current value of the property
3869 public Color getGridColor()
3875 * Get the value of the <code>intercellSpacing</code> property.
3877 * @return The current value of the property
3879 public Dimension getIntercellSpacing()
3881 return new Dimension(columnModel.getColumnMargin(), rowMargin);
3885 * Get the value of the {@link #preferredViewportSize} property.
3887 * @return The current value of the property
3889 public Dimension getPreferredScrollableViewportSize()
3891 return preferredViewportSize;
3895 * Get the value of the {@link #selectionBackground} property.
3897 * @return The current value of the property
3899 public Color getSelectionBackground()
3901 return selectionBackground;
3905 * Get the value of the {@link #selectionForeground} property.
3907 * @return The current value of the property
3909 public Color getSelectionForeground()
3911 return selectionForeground;
3915 * Get the value of the {@link #showHorizontalLines} property.
3917 * @return The current value of the property
3919 public boolean getShowHorizontalLines()
3921 return showHorizontalLines;
3925 * Get the value of the {@link #showVerticalLines} property.
3927 * @return The current value of the property
3929 public boolean getShowVerticalLines()
3931 return showVerticalLines;
3935 * Get the value of the {@link #tableHeader} property.
3937 * @return The current value of the property
3939 public JTableHeader getTableHeader()
3945 * Removes specified column from displayable columns of this table.
3947 * @param column column to removed
3949 public void removeColumn(TableColumn column)
3951 columnModel.removeColumn(column);
3955 * Moves column at the specified index to new given location.
3957 * @param column index of the column to move
3958 * @param targetColumn index specifying new location of the column
3960 public void moveColumn(int column,int targetColumn)
3962 columnModel.moveColumn(column, targetColumn);
3966 * Set the value of the {@link #autoCreateColumnsFromModel} flag. If the
3967 * flag changes from <code>false</code> to <code>true</code>, the
3968 * {@link #createDefaultColumnsFromModel()} method is called.
3970 * @param autoCreate the new value of the flag.
3972 public void setAutoCreateColumnsFromModel(boolean autoCreate)
3974 if (autoCreateColumnsFromModel != autoCreate)
3976 autoCreateColumnsFromModel = autoCreate;
3978 createDefaultColumnsFromModel();
3983 * Set the value of the {@link #autoResizeMode} property.
3985 * @param a The new value of the autoResizeMode property
3987 public void setAutoResizeMode(int a)
3995 * Sets the height for all rows in the table. If you want to change the
3996 * height of a single row instead, use {@link #setRowHeight(int, int)}.
3998 * @param r the height to set for all rows
4000 * @see #getRowHeight()
4001 * @see #setRowHeight(int, int)
4002 * @see #getRowHeight(int)
4004 public void setRowHeight(int r)
4007 throw new IllegalArgumentException();
4009 clientRowHeightSet = true;
4018 * Sets the height of a single row in the table.
4020 * @param rh the new row height
4021 * @param row the row to change the height of
4023 public void setRowHeight(int row, int rh)
4025 if (rowHeights == null)
4027 rowHeights = new SizeSequence(getRowCount(), rowHeight);
4029 rowHeights.setSize(row, rh);
4033 * Set the value of the {@link #rowMargin} property.
4035 * @param r The new value of the rowMargin property
4037 public void setRowMargin(int r)
4045 * Set the value of the {@link #rowSelectionAllowed} property.
4047 * @param r The new value of the rowSelectionAllowed property
4049 * @see #getRowSelectionAllowed()
4051 public void setRowSelectionAllowed(boolean r)
4053 if (rowSelectionAllowed != r)
4055 rowSelectionAllowed = r;
4056 firePropertyChange("rowSelectionAllowed", !r, r);
4062 * Set the value of the {@link #cellSelectionEnabled} property.
4064 * @param c The new value of the cellSelectionEnabled property
4066 public void setCellSelectionEnabled(boolean c)
4068 setColumnSelectionAllowed(c);
4069 setRowSelectionAllowed(c);
4070 // for backward-compatibility sake:
4071 cellSelectionEnabled = true;
4075 * <p>Set the value of the {@link #dataModel} property.</p>
4077 * <p>Unregister <code>this</code> as a {@link TableModelListener} from
4078 * previous {@link #dataModel} and register it with new parameter
4079 * <code>m</code>.</p>
4081 * @param m The new value of the model property
4083 public void setModel(TableModel m)
4085 // Throw exception is m is null.
4087 throw new IllegalArgumentException();
4089 // Don't do anything if setting the current model again.
4093 TableModel oldModel = dataModel;
4095 // Remove table as TableModelListener from old model.
4096 if (dataModel != null)
4097 dataModel.removeTableModelListener(this);
4104 // Add table as TableModelListener to new model.
4105 dataModel.addTableModelListener(this);
4107 // Notify the tableChanged method.
4108 tableChanged(new TableModelEvent(dataModel,
4109 TableModelEvent.HEADER_ROW));
4111 // Automatically create columns.
4112 if (autoCreateColumnsFromModel)
4113 createDefaultColumnsFromModel();
4116 // This property is bound, so we fire a property change event.
4117 firePropertyChange("model", oldModel, dataModel);
4125 * <p>Set the value of the {@link #columnModel} property.</p>
4127 * <p>Unregister <code>this</code> as a {@link TableColumnModelListener}
4128 * from previous {@link #columnModel} and register it with new parameter
4129 * <code>c</code>.</p>
4131 * @param c The new value of the columnModel property
4133 public void setColumnModel(TableColumnModel c)
4136 throw new IllegalArgumentException();
4137 TableColumnModel tmp = columnModel;
4139 tmp.removeColumnModelListener(this);
4141 c.addColumnModelListener(this);
4143 if (dataModel != null && columnModel != null)
4145 int ncols = getColumnCount();
4147 for (int i = 0; i < ncols; ++i)
4149 column = columnModel.getColumn(i);
4150 if (column.getHeaderValue()==null)
4151 column.setHeaderValue(dataModel.getColumnName(i));
4155 // according to Sun's spec we also have to set the tableHeader's
4156 // column model here
4157 if (tableHeader != null)
4158 tableHeader.setColumnModel(c);
4165 * Set the value of the <code>columnSelectionAllowed</code> property.
4167 * @param c The new value of the property
4169 * @see #getColumnSelectionAllowed()
4171 public void setColumnSelectionAllowed(boolean c)
4173 if (columnModel.getColumnSelectionAllowed() != c)
4175 columnModel.setColumnSelectionAllowed(c);
4176 firePropertyChange("columnSelectionAllowed", !c, c);
4182 * <p>Set the value of the {@link #selectionModel} property.</p>
4184 * <p>Unregister <code>this</code> as a {@link ListSelectionListener}
4185 * from previous {@link #selectionModel} and register it with new
4186 * parameter <code>s</code>.</p>
4188 * @param s The new value of the selectionModel property
4190 public void setSelectionModel(ListSelectionModel s)
4193 throw new IllegalArgumentException();
4194 ListSelectionModel tmp = selectionModel;
4196 tmp.removeListSelectionListener(this);
4198 s.addListSelectionListener(this);
4204 * Set the value of the <code>selectionMode</code> property by
4205 * delegation to the {@link #selectionModel} field. The same selection
4206 * mode is set for row and column selection models.
4208 * @param s The new value of the property
4210 public void setSelectionMode(int s)
4212 selectionModel.setSelectionMode(s);
4213 columnModel.getSelectionModel().setSelectionMode(s);
4219 * <p>Set the value of the {@link #cellEditor} property.</p>
4221 * <p>Unregister <code>this</code> as a {@link CellEditorListener} from
4222 * previous {@link #cellEditor} and register it with new parameter
4223 * <code>c</code>.</p>
4225 * @param c The new value of the cellEditor property
4227 public void setCellEditor(TableCellEditor c)
4229 TableCellEditor tmp = cellEditor;
4231 tmp.removeCellEditorListener(this);
4233 c.addCellEditorListener(this);
4238 * Set the value of the {@link #dragEnabled} property.
4240 * @param d The new value of the dragEnabled property
4242 public void setDragEnabled(boolean d)
4248 * Set the value of the {@link #gridColor} property.
4250 * @param g The new value of the gridColor property
4252 public void setGridColor(Color g)
4259 * Set the value of the <code>intercellSpacing</code> property.
4261 * @param i The new value of the intercellSpacing property
4263 public void setIntercellSpacing(Dimension i)
4265 rowMargin = i.height;
4266 columnModel.setColumnMargin(i.width);
4271 * Set the value of the {@link #preferredViewportSize} property.
4273 * @param p The new value of the preferredViewportSize property
4275 public void setPreferredScrollableViewportSize(Dimension p)
4277 preferredViewportSize = p;
4283 * <p>Set the value of the {@link #selectionBackground} property.</p>
4285 * <p>Fire a PropertyChangeEvent with name {@link
4286 * #SELECTION_BACKGROUND_CHANGED_PROPERTY} to registered listeners, if
4287 * selectionBackground changed.</p>
4289 * @param s The new value of the selectionBackground property
4291 public void setSelectionBackground(Color s)
4293 Color tmp = selectionBackground;
4294 selectionBackground = s;
4295 if (((tmp == null && s != null)
4296 || (s == null && tmp != null)
4297 || (tmp != null && s != null && !tmp.equals(s))))
4298 firePropertyChange(SELECTION_BACKGROUND_CHANGED_PROPERTY, tmp, s);
4303 * <p>Set the value of the {@link #selectionForeground} property.</p>
4305 * <p>Fire a PropertyChangeEvent with name {@link
4306 * #SELECTION_FOREGROUND_CHANGED_PROPERTY} to registered listeners, if
4307 * selectionForeground changed.</p>
4309 * @param s The new value of the selectionForeground property
4311 public void setSelectionForeground(Color s)
4313 Color tmp = selectionForeground;
4314 selectionForeground = s;
4315 if (((tmp == null && s != null)
4316 || (s == null && tmp != null)
4317 || (tmp != null && s != null && !tmp.equals(s))))
4318 firePropertyChange(SELECTION_FOREGROUND_CHANGED_PROPERTY, tmp, s);
4323 * Set the value of the <code>showGrid</code> property.
4325 * @param s The new value of the showGrid property
4327 public void setShowGrid(boolean s)
4329 setShowVerticalLines(s);
4330 setShowHorizontalLines(s);
4334 * Set the value of the {@link #showHorizontalLines} property.
4336 * @param s The new value of the showHorizontalLines property
4338 public void setShowHorizontalLines(boolean s)
4340 showHorizontalLines = s;
4345 * Set the value of the {@link #showVerticalLines} property.
4347 * @param s The new value of the showVerticalLines property
4349 public void setShowVerticalLines(boolean s)
4351 showVerticalLines = s;
4356 * Set the value of the {@link #tableHeader} property.
4358 * @param t The new value of the tableHeader property
4360 public void setTableHeader(JTableHeader t)
4362 if (tableHeader != null)
4363 tableHeader.setTable(null);
4365 if (tableHeader != null)
4366 tableHeader.setTable(this);
4371 protected void configureEnclosingScrollPane()
4373 JScrollPane jsp = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, this);
4374 if (jsp != null && tableHeader != null)
4376 jsp.setColumnHeaderView(tableHeader);
4380 protected void unconfigureEnclosingScrollPane()
4382 JScrollPane jsp = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, this);
4385 jsp.setColumnHeaderView(null);
4390 public void addNotify()
4393 configureEnclosingScrollPane();
4396 public void removeNotify()
4399 unconfigureEnclosingScrollPane();
4404 * This distributes the superfluous width in a table evenly on its columns.
4406 * The implementation used here is different to that one described in
4407 * the JavaDocs. It is much simpler, and seems to work very well.
4409 * TODO: correctly implement the algorithm described in the JavaDoc
4411 private void distributeSpill(TableColumn[] cols, int spill)
4413 int average = spill / cols.length;
4414 for (int i = 0; i < cols.length; i++)
4416 if (cols[i] != null)
4417 cols[i].setWidth(cols[i].getPreferredWidth() + average);
4422 * This distributes the superfluous width in a table, setting the width of the
4423 * column being resized strictly to its preferred width.
4425 private void distributeSpillResizing(TableColumn[] cols, int spill,
4426 TableColumn resizeIt)
4429 if (cols.length != 1)
4430 average = spill / (cols.length-1);
4431 for (int i = 0; i < cols.length; i++)
4433 if (cols[i] != null && !cols[i].equals(resizeIt))
4434 cols[i].setWidth(cols[i].getPreferredWidth() + average);
4436 resizeIt.setWidth(resizeIt.getPreferredWidth());
4440 * Set the widths of all columns, taking they preferred widths into
4441 * consideration. The excess space, if any, will be distrubuted between
4442 * all columns. This method also handles special cases when one of the
4443 * collumns is currently being resized.
4445 * @see TableColumn#setPreferredWidth(int)
4447 public void doLayout()
4449 TableColumn resizingColumn = null;
4451 int ncols = columnModel.getColumnCount();
4458 if (tableHeader != null)
4459 resizingColumn = tableHeader.getResizingColumn();
4461 for (int i = 0; i < ncols; ++i)
4463 TableColumn col = columnModel.getColumn(i);
4464 int p = col.getPreferredWidth();
4466 if (resizingColumn == col)
4470 int spill = getWidth() - prefSum;
4472 if (resizingColumn != null)
4475 TableColumn [] cols;
4477 switch (getAutoResizeMode())
4479 case AUTO_RESIZE_LAST_COLUMN:
4480 col = columnModel.getColumn(ncols-1);
4481 col.setWidth(col.getPreferredWidth() + spill);
4484 case AUTO_RESIZE_NEXT_COLUMN:
4485 col = columnModel.getColumn(ncols-1);
4486 col.setWidth(col.getPreferredWidth() + spill);
4489 case AUTO_RESIZE_ALL_COLUMNS:
4490 cols = new TableColumn[ncols];
4491 for (int i = 0; i < ncols; ++i)
4492 cols[i] = columnModel.getColumn(i);
4493 distributeSpillResizing(cols, spill, resizingColumn);
4496 case AUTO_RESIZE_SUBSEQUENT_COLUMNS:
4498 // Subtract the width of the non-resized columns from the spill.
4502 for (int i = 0; i < rCol; i++)
4504 column = columnModel.getColumn(i);
4505 w += column.getWidth();
4506 wp+= column.getPreferredWidth();
4509 // The number of columns right from the column being resized.
4510 int n = ncols-rCol-1;
4513 // If there are any columns on the right sied to resize.
4514 spill = (getWidth()-w) - (prefSum-wp);
4515 int average = spill / n;
4517 // For all columns right from the column being resized:
4518 for (int i = rCol+1; i < ncols; i++)
4520 column = columnModel.getColumn(i);
4521 column.setWidth(column.getPreferredWidth() + average);
4524 resizingColumn.setWidth(resizingColumn.getPreferredWidth());
4527 case AUTO_RESIZE_OFF:
4529 int prefWidth = resizingColumn.getPreferredWidth();
4530 resizingColumn.setWidth(prefWidth);
4535 TableColumn[] cols = new TableColumn[ncols];
4537 for (int i = 0; i < ncols; ++i)
4538 cols[i] = columnModel.getColumn(i);
4540 distributeSpill(cols, spill);
4543 if (editorComp!=null)
4544 moveToCellBeingEdited(editorComp);
4546 int leftBoundary = getLeftResizingBoundary();
4547 int width = getWidth() - leftBoundary;
4548 repaint(leftBoundary, 0, width, getHeight());
4549 if (tableHeader != null)
4550 tableHeader.repaint(leftBoundary, 0, width, tableHeader.getHeight());
4554 * Get the left boundary of the rectangle which changes during the column
4557 int getLeftResizingBoundary()
4559 if (tableHeader == null || getAutoResizeMode() == AUTO_RESIZE_ALL_COLUMNS)
4563 TableColumn resizingColumn = tableHeader.getResizingColumn();
4564 if (resizingColumn == null)
4567 int rc = convertColumnIndexToView(resizingColumn.getModelIndex());
4570 for (int i = 0; i < rc; i++)
4571 p += columnModel.getColumn(i).getWidth();
4579 * @deprecated Replaced by <code>doLayout()</code>
4581 public void sizeColumnsToFit(boolean lastColumnOnly)
4587 * Obsolete since JDK 1.4. Please use <code>doLayout()</code>.
4589 public void sizeColumnsToFit(int resizingColumn)
4594 public String getUIClassID()
4600 * This method returns the table's UI delegate.
4602 * @return The table's UI delegate.
4604 public TableUI getUI()
4606 return (TableUI) ui;
4610 * This method sets the table's UI delegate.
4612 * @param ui The table's UI delegate.
4614 public void setUI(TableUI ui)
4617 // The editors and renderers must be recreated because they constructors
4618 // may use the look and feel properties.
4619 createDefaultEditors();
4620 createDefaultRenderers();
4623 public void updateUI()
4625 setUI((TableUI) UIManager.getUI(this));
4629 * Get the class (datatype) of the column. The cells are rendered and edited
4630 * differently, depending from they data type.
4632 * @param column the column (not the model index).
4634 * @return the class, defining data type of that column (String.class for
4635 * String, Boolean.class for boolean and so on).
4637 public Class<?> getColumnClass(int column)
4639 return getModel().getColumnClass(convertColumnIndexToModel(column));
4643 * Get the name of the column. If the column has the column identifier set,
4644 * the return value is the result of the .toString() method call on that
4645 * identifier. If the identifier is not explicitly set, the returned value
4647 * {@link javax.swing.table.AbstractTableModel#getColumnName(int)}.
4649 * @param column the column
4651 * @return the name of that column.
4653 public String getColumnName(int column)
4655 int modelColumn = columnModel.getColumn(column).getModelIndex();
4656 return dataModel.getColumnName(modelColumn);
4660 * Get the column, currently being edited
4662 * @return the column, currently being edited.
4664 public int getEditingColumn()
4666 return editingColumn;
4670 * Set the column, currently being edited
4672 * @param column the column, currently being edited.
4674 public void setEditingColumn(int column)
4676 editingColumn = column;
4680 * Get the row currently being edited.
4682 * @return the row, currently being edited.
4684 public int getEditingRow()
4690 * Set the row currently being edited.
4692 * @param row the row, that will be edited
4694 public void setEditingRow(int row)
4700 * Get the editor component that is currently editing one of the cells
4702 * @return the editor component or null, if none of the cells is being
4705 public Component getEditorComponent()
4711 * Check if one of the table cells is currently being edited.
4713 * @return true if there is a cell being edited.
4715 public boolean isEditing()
4717 return editorComp != null;
4721 * Set the default editor for the given column class (column data type).
4722 * By default, String is handled by text field and Boolean is handled by
4725 * @param columnClass the column data type
4726 * @param editor the editor that will edit this data type
4728 * @see TableModel#getColumnClass(int)
4730 public void setDefaultEditor(Class<?> columnClass, TableCellEditor editor)
4733 defaultEditorsByColumnClass.put(columnClass, editor);
4735 defaultEditorsByColumnClass.remove(columnClass);
4738 public void addColumnSelectionInterval(int index0, int index1)
4740 if ((index0 < 0 || index0 > (getColumnCount()-1)
4741 || index1 < 0 || index1 > (getColumnCount()-1)))
4742 throw new IllegalArgumentException("Column index out of range.");
4744 getColumnModel().getSelectionModel().addSelectionInterval(index0, index1);
4747 public void addRowSelectionInterval(int index0, int index1)
4749 if ((index0 < 0 || index0 > (getRowCount()-1)
4750 || index1 < 0 || index1 > (getRowCount()-1)))
4751 throw new IllegalArgumentException("Row index out of range.");
4753 getSelectionModel().addSelectionInterval(index0, index1);
4756 public void setColumnSelectionInterval(int index0, int index1)
4758 if ((index0 < 0 || index0 > (getColumnCount()-1)
4759 || index1 < 0 || index1 > (getColumnCount()-1)))
4760 throw new IllegalArgumentException("Column index out of range.");
4762 getColumnModel().getSelectionModel().setSelectionInterval(index0, index1);
4765 public void setRowSelectionInterval(int index0, int index1)
4767 if ((index0 < 0 || index0 > (getRowCount()-1)
4768 || index1 < 0 || index1 > (getRowCount()-1)))
4769 throw new IllegalArgumentException("Row index out of range.");
4771 getSelectionModel().setSelectionInterval(index0, index1);
4774 public void removeColumnSelectionInterval(int index0, int index1)
4776 if ((index0 < 0 || index0 > (getColumnCount()-1)
4777 || index1 < 0 || index1 > (getColumnCount()-1)))
4778 throw new IllegalArgumentException("Column index out of range.");
4780 getColumnModel().getSelectionModel().removeSelectionInterval(index0, index1);
4783 public void removeRowSelectionInterval(int index0, int index1)
4785 if ((index0 < 0 || index0 > (getRowCount()-1)
4786 || index1 < 0 || index1 > (getRowCount()-1)))
4787 throw new IllegalArgumentException("Row index out of range.");
4789 getSelectionModel().removeSelectionInterval(index0, index1);
4793 * Checks if the given column is selected.
4795 * @param column the column
4797 * @return true if the column is selected (as reported by the selection
4798 * model, associated with the column model), false otherwise.
4800 public boolean isColumnSelected(int column)
4802 return getColumnModel().getSelectionModel().isSelectedIndex(column);
4806 * Checks if the given row is selected.
4808 * @param row the row
4810 * @return true if the row is selected (as reported by the selection model),
4813 public boolean isRowSelected(int row)
4815 return getSelectionModel().isSelectedIndex(row);
4819 * Checks if the given cell is selected. The cell is selected if both
4820 * the cell row and the cell column are selected.
4822 * @param row the cell row
4823 * @param column the cell column
4825 * @return true if the cell is selected, false otherwise
4827 public boolean isCellSelected(int row, int column)
4829 return isRowSelected(row) && isColumnSelected(column);
4835 public void selectAll()
4837 // The table is empty - nothing to do!
4838 if (getRowCount() == 0 || getColumnCount() == 0)
4841 // rowLead and colLead store the current lead selection indices
4842 int rowLead = selectionModel.getLeadSelectionIndex();
4843 int colLead = getColumnModel().getSelectionModel().getLeadSelectionIndex();
4844 // the following calls to setSelectionInterval change the lead selection
4846 setColumnSelectionInterval(0, getColumnCount() - 1);
4847 setRowSelectionInterval(0, getRowCount() - 1);
4848 // the following addSelectionInterval calls restore the lead selection
4849 // indices to their previous values
4850 addColumnSelectionInterval(colLead,colLead);
4851 addRowSelectionInterval(rowLead, rowLead);
4855 * Get the cell value at the given position.
4857 * @param row the row to get the value
4858 * @param column the actual column number (not the model index)
4861 * @return the cell value, as returned by model.
4863 public Object getValueAt(int row, int column)
4865 return dataModel.getValueAt(row, convertColumnIndexToModel(column));
4869 * Set value for the cell at the given position. The modified cell is
4872 * @param value the value to set
4873 * @param row the row of the cell being modified
4874 * @param column the column of the cell being modified
4876 public void setValueAt(Object value, int row, int column)
4878 dataModel.setValueAt(value, row, convertColumnIndexToModel(column));
4880 repaint(getCellRect(row, column, true));
4884 * Get table column with the given identified.
4886 * @param identifier the column identifier
4888 * @return the table column with this identifier
4890 * @throws IllegalArgumentException if <code>identifier</code> is
4891 * <code>null</code> or there is no column with that identifier.
4893 * @see TableColumn#setIdentifier(Object)
4895 public TableColumn getColumn(Object identifier)
4897 return columnModel.getColumn(columnModel.getColumnIndex(identifier));
4901 * Returns <code>true</code> if the specified cell is editable, and
4902 * <code>false</code> otherwise.
4904 * @param row the row index.
4905 * @param column the column index.
4907 * @return true if the cell is editable, false otherwise.
4909 public boolean isCellEditable(int row, int column)
4911 return dataModel.isCellEditable(row, convertColumnIndexToModel(column));
4915 * Clears any existing columns from the <code>JTable</code>'s
4916 * {@link TableColumnModel} and creates new columns to match the values in
4917 * the data ({@link TableModel}) used by the table.
4919 * @see #setAutoCreateColumnsFromModel(boolean)
4921 public void createDefaultColumnsFromModel()
4923 assert columnModel != null : "The columnModel must not be null.";
4925 // remove existing columns
4926 int columnIndex = columnModel.getColumnCount() - 1;
4927 while (columnIndex >= 0)
4929 columnModel.removeColumn(columnModel.getColumn(columnIndex));
4933 // add new columns to match the TableModel
4934 int columnCount = dataModel.getColumnCount();
4935 for (int c = 0; c < columnCount; c++)
4937 TableColumn column = new TableColumn(c);
4938 column.setIdentifier(dataModel.getColumnName(c));
4939 column.setHeaderValue(dataModel.getColumnName(c));
4940 columnModel.addColumn(column);
4941 column.addPropertyChangeListener(tableColumnPropertyChangeHandler);
4945 public void changeSelection (int rowIndex, int columnIndex, boolean toggle, boolean extend)
4947 if (toggle && extend)
4949 // Leave the selection state as is, but move the anchor
4950 // index to the specified location
4951 selectionModel.setAnchorSelectionIndex(rowIndex);
4952 getColumnModel().getSelectionModel().setAnchorSelectionIndex(columnIndex);
4956 // Toggle the state of the specified cell
4957 if (isCellSelected(rowIndex,columnIndex))
4959 selectionModel.removeSelectionInterval(rowIndex,rowIndex);
4960 getColumnModel().getSelectionModel().removeSelectionInterval(columnIndex,columnIndex);
4964 selectionModel.addSelectionInterval(rowIndex,rowIndex);
4965 getColumnModel().getSelectionModel().addSelectionInterval(columnIndex,columnIndex);
4970 // Extend the previous selection from the anchor to the
4971 // specified cell, clearing all other selections
4972 selectionModel.setLeadSelectionIndex(rowIndex);
4973 getColumnModel().getSelectionModel().setLeadSelectionIndex(columnIndex);
4977 // Clear the previous selection and ensure the new cell
4979 selectionModel.clearSelection();
4980 selectionModel.setSelectionInterval(rowIndex,rowIndex);
4981 getColumnModel().getSelectionModel().clearSelection();
4982 getColumnModel().getSelectionModel().setSelectionInterval(columnIndex, columnIndex);
4989 * Programmatically starts editing the specified cell.
4991 * @param row the row of the cell to edit.
4992 * @param column the column of the cell to edit.
4994 public boolean editCellAt(int row, int column)
4996 // Complete the previous editing session, if still active.
4998 editingStopped(new ChangeEvent("editingStopped"));
5000 TableCellEditor editor = getCellEditor(row, column);
5002 // The boolean values are inverted by the single click without the
5003 // real editing session.
5004 if (editor == booleanInvertingEditor && isCellEditable(row, column))
5006 if (Boolean.TRUE.equals(getValueAt(row, column)))
5007 setValueAt(Boolean.FALSE, row, column);
5009 setValueAt(Boolean.TRUE, row, column);
5015 editingColumn = column;
5017 setCellEditor(editor);
5018 editorComp = prepareEditor(cellEditor, row, column);
5020 // Remove the previous editor components, if present. Only one
5021 // editor component at time is allowed in the table.
5024 moveToCellBeingEdited(editorComp);
5025 scrollRectToVisible(editorComp.getBounds());
5026 editorComp.requestFocusInWindow();
5028 // Deliver the should select event.
5029 return editor.shouldSelectCell(null);
5034 * Move the given component under the cell being edited.
5035 * The table must be in the editing mode.
5037 * @param component the component to move.
5039 private void moveToCellBeingEdited(Component component)
5041 Rectangle r = getCellRect(editingRow, editingColumn, true);
5042 // Adjust bounding box of the editing component, so that it lies
5043 // 'above' the grid on all edges, not only right and bottom.
5044 // The table grid is painted only at the right and bottom edge of a cell.
5049 component.setBounds(r);
5053 * Programmatically starts editing the specified cell.
5055 * @param row the row of the cell to edit.
5056 * @param column the column of the cell to edit.
5058 public boolean editCellAt (int row, int column, EventObject e)
5060 return editCellAt(row, column);
5064 * Discards the editor object.
5066 public void removeEditor()
5068 editingStopped(new ChangeEvent(this));
5072 * Prepares the editor by querying for the value and selection state of the
5073 * cell at (row, column).
5075 * @param editor the TableCellEditor to set up
5076 * @param row the row of the cell to edit
5077 * @param column the column of the cell to edit
5078 * @return the Component being edited
5080 public Component prepareEditor (TableCellEditor editor, int row, int column)
5082 return editor.getTableCellEditorComponent
5083 (this, getValueAt(row, column), isCellSelected(row, column), row, column);
5087 * This revalidates the <code>JTable</code> and queues a repaint.
5089 protected void resizeAndRepaint()
5096 * Sets whether cell editors of this table should receive keyboard focus
5097 * when the editor is activated by a keystroke. The default setting is
5098 * <code>false</code> which means that the table should keep the keyboard
5099 * focus until the cell is selected by a mouse click.
5101 * @param value the value to set
5105 public void setSurrendersFocusOnKeystroke(boolean value)
5107 // TODO: Implement functionality of this property (in UI impl).
5108 surrendersFocusOnKeystroke = value;
5112 * Returns whether cell editors of this table should receive keyboard focus
5113 * when the editor is activated by a keystroke. The default setting is
5114 * <code>false</code> which means that the table should keep the keyboard
5115 * focus until the cell is selected by a mouse click.
5117 * @return whether cell editors of this table should receive keyboard focus
5118 * when the editor is activated by a keystroke
5122 public boolean getSurrendersFocusOnKeystroke()
5124 // TODO: Implement functionality of this property (in UI impl).
5125 return surrendersFocusOnKeystroke;
5130 * {@link LookAndFeel#installProperty(JComponent, String, Object)}.
5132 * @param propertyName the name of the property
5133 * @param value the value of the property
5135 * @throws IllegalArgumentException if the specified property cannot be set
5137 * @throws ClassCastException if the property value does not match the
5139 * @throws NullPointerException if <code>c</code> or
5140 * <code>propertyValue</code> is <code>null</code>
5142 void setUIProperty(String propertyName, Object value)
5144 if (propertyName.equals("rowHeight"))
5146 if (! clientRowHeightSet)
5148 setRowHeight(((Integer) value).intValue());
5149 clientRowHeightSet = false;
5154 super.setUIProperty(propertyName, value);