1 /* JTable.java -- 2 Copyright (C) 2002, 2004, 2005, 2006 Free Software Foundation, Inc. 3 4 This file is part of GNU Classpath. 5 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) 9 any later version. 10 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. 15 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 19 02110-1301 USA. 20 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 24 combination. 25 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. */ 37 38 39 package javax.swing; 40 41 import java.awt.Color; 42 import java.awt.Component; 43 import java.awt.Cursor; 44 import java.awt.Dimension; 45 import java.awt.Font; 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; 59 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; 88 89 /** 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. 97 */ 98 public class JTable 99 extends JComponent 100 implements TableModelListener, Scrollable, TableColumnModelListener, 101 ListSelectionListener, CellEditorListener, Accessible 102 { 103 /** 104 * Provides accessibility support for <code>JTable</code>. 105 * 106 * @author Roman Kennke (kennke@aicas.com) 107 */ 108 protected class AccessibleJTable 109 extends AccessibleJComponent 110 implements AccessibleSelection, ListSelectionListener, TableModelListener, 111 TableColumnModelListener, CellEditorListener, PropertyChangeListener, 112 AccessibleExtendedTable 113 { 114 115 /** 116 * Provides accessibility support for table cells. 117 * 118 * @author Roman Kennke (kennke@aicas.com) 119 */ 120 protected class AccessibleJTableCell 121 extends AccessibleContext 122 implements Accessible, AccessibleComponent 123 { 124 125 /** 126 * The table of this cell. 127 */ 128 private JTable table; 129 130 /** 131 * The row index of this cell. 132 */ 133 private int row; 134 135 /** 136 * The column index of this cell. 137 */ 138 private int column; 139 140 /** 141 * The index of this cell inside the AccessibleJTable parent. 142 */ 143 private int index; 144 145 /** 146 * Creates a new <code>AccessibleJTableCell</code>. 147 * 148 * @param t the table 149 * @param r the row 150 * @param c the column 151 * @param i the index of this cell inside the accessible table parent 152 */ AccessibleJTableCell(JTable t, int r, int c, int i)153 public AccessibleJTableCell(JTable t, int r, int c, int i) 154 { 155 table = t; 156 row = r; 157 column = c; 158 index = i; 159 } 160 161 /** 162 * Returns the accessible row for the table cell. 163 * 164 * @return the accessible row for the table cell 165 */ getAccessibleRole()166 public AccessibleRole getAccessibleRole() 167 { 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; 172 } 173 174 /** 175 * Returns the accessible state set of this accessible table cell. 176 * 177 * @return the accessible state set of this accessible table cell 178 */ getAccessibleStateSet()179 public AccessibleStateSet getAccessibleStateSet() 180 { 181 AccessibleStateSet state = new AccessibleStateSet(); 182 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); 188 189 // Figure out SELECTED state. 190 if (isCellSelected(row, column)) 191 state.add(AccessibleState.SELECTED); 192 193 // Figure out ACTIVE state. 194 if (row == getSelectedRow() && column == getSelectedColumn()) 195 state.add(AccessibleState.ACTIVE); 196 197 // TRANSIENT seems to be always set in the RI. 198 state.add(AccessibleState.TRANSIENT); 199 200 // TODO: Any other state to handle here? 201 return state; 202 } 203 204 /** 205 * Returns the index of this cell in the parent object. 206 * 207 * @return the index of this cell in the parent object 208 */ getAccessibleIndexInParent()209 public int getAccessibleIndexInParent() 210 { 211 return index; 212 } 213 214 /** 215 * Returns the number of children of this object. Table cells cannot have 216 * children, so we return <code>0</code> here. 217 * 218 * @return <code>0</code> 219 */ getAccessibleChildrenCount()220 public int getAccessibleChildrenCount() 221 { 222 return 0; 223 } 224 225 /** 226 * Returns the accessible child at index <code>i</code>. Table cells 227 * don't have children, so we return <code>null</code> here. 228 * 229 * @return <code>null</code> 230 */ getAccessibleChild(int i)231 public Accessible getAccessibleChild(int i) 232 { 233 return null; 234 } 235 236 /** 237 * Returns the locale setting for this accessible table cell. 238 * 239 * @return the locale setting for this accessible table cell 240 */ getLocale()241 public Locale getLocale() 242 { 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; 246 } 247 248 /** 249 * Returns the accessible context of this table cell. Since accessible 250 * table cells are their own accessible context, we return 251 * <code>this</code>. 252 * 253 * @return the accessible context of this table cell 254 */ getAccessibleContext()255 public AccessibleContext getAccessibleContext() 256 { 257 return this; 258 } 259 260 /** 261 * Returns the background color of this cell. 262 * 263 * @return the background color of this cell 264 */ getBackground()265 public Color getBackground() 266 { 267 return table.getBackground(); 268 } 269 270 /** 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. 274 * 275 * @param color not used 276 */ setBackground(Color color)277 public void setBackground(Color color) 278 { 279 // This method does nothing. See API comments. 280 } 281 282 /** 283 * Returns the foreground color of the table cell. 284 * 285 * @return the foreground color of the table cell 286 */ getForeground()287 public Color getForeground() 288 { 289 return table.getForeground(); 290 } 291 292 /** 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. 296 * 297 * @param color not used 298 */ setForeground(Color color)299 public void setForeground(Color color) 300 { 301 // This method does nothing. See API comments. 302 } 303 304 /** 305 * Returns the cursor for this table cell. 306 * 307 * @return the cursor for this table cell 308 */ getCursor()309 public Cursor getCursor() 310 { 311 return table.getCursor(); 312 } 313 314 /** 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. 318 * 319 * @param cursor not used 320 */ setCursor(Cursor cursor)321 public void setCursor(Cursor cursor) 322 { 323 // This method does nothing. See API comments. 324 } 325 326 /** 327 * Returns the font of the table cell. 328 * 329 * @return the font of the table cell 330 */ getFont()331 public Font getFont() 332 { 333 return table.getFont(); 334 } 335 336 /** 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. 340 * 341 * @param font not used 342 */ setFont(Font font)343 public void setFont(Font font) 344 { 345 // This method does nothing. See API comments. 346 } 347 348 /** 349 * Returns the font metrics for a specified font. 350 * 351 * @param font the font for which we return the metrics 352 * 353 * @return the font metrics for a specified font 354 */ getFontMetrics(Font font)355 public FontMetrics getFontMetrics(Font font) 356 { 357 return table.getFontMetrics(font); 358 } 359 360 /** 361 * Returns <code>true</code> if this table cell is enabled, 362 * <code>false</code> otherwise. 363 * 364 * @return <code>true</code> if this table cell is enabled, 365 * <code>false</code> otherwise 366 */ isEnabled()367 public boolean isEnabled() 368 { 369 return table.isEnabled(); 370 } 371 372 /** 373 * Table cells cannot be disabled or enabled individually, so this method 374 * does nothing. Set the enabled flag on the table itself. 375 * 376 * @param b not used here 377 */ setEnabled(boolean b)378 public void setEnabled(boolean b) 379 { 380 // This method does nothing. See API comments. 381 } 382 383 /** 384 * Returns <code>true</code> if this cell is visible, <code>false</code> 385 * otherwise. 386 * 387 * @return <code>true</code> if this cell is visible, <code>false</code> 388 * otherwise 389 */ isVisible()390 public boolean isVisible() 391 { 392 return table.isVisible(); 393 } 394 395 /** 396 * The visibility cannot be set on individual table cells, so this method 397 * does nothing. Set the visibility on the table itself. 398 * 399 * @param b not used 400 */ setVisible(boolean b)401 public void setVisible(boolean b) 402 { 403 // This method does nothing. See API comments. 404 } 405 406 /** 407 * Returns <code>true</code> if this table cell is currently showing on 408 * screen. 409 * 410 * @return <code>true</code> if this table cell is currently showing on 411 * screen 412 */ isShowing()413 public boolean isShowing() 414 { 415 return table.isShowing(); 416 } 417 418 /** 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 422 * of the table cell. 423 * 424 * @return <code>true</code> if this table cell contains the location 425 * at <code>point</code>, <code>false</code> otherwise 426 */ contains(Point point)427 public boolean contains(Point point) 428 { 429 Rectangle cellRect = table.getCellRect(row, column, true); 430 cellRect.x = 0; 431 cellRect.y = 0; 432 return cellRect.contains(point); 433 } 434 435 /** 436 * Returns the screen location of the table cell. 437 * 438 * @return the screen location of the table cell 439 */ getLocationOnScreen()440 public Point getLocationOnScreen() 441 { 442 Point tableLoc = table.getLocationOnScreen(); 443 Rectangle cellRect = table.getCellRect(row, column, true); 444 tableLoc.x += cellRect.x; 445 tableLoc.y += cellRect.y; 446 return tableLoc; 447 } 448 449 /** 450 * Returns the location of this cell relative to the table's bounds. 451 * 452 * @return the location of this cell relative to the table's bounds 453 */ getLocation()454 public Point getLocation() 455 { 456 Rectangle cellRect = table.getCellRect(row, column, true); 457 return new Point(cellRect.x, cellRect.y); 458 } 459 460 /** 461 * The location of the table cells cannot be manipulated directly, so 462 * this method does nothing. 463 * 464 * @param point not used 465 */ setLocation(Point point)466 public void setLocation(Point point) 467 { 468 // This method does nothing. See API comments. 469 } 470 471 /** 472 * Returns the bounds of the cell relative to its table. 473 * 474 * @return the bounds of the cell relative to its table 475 */ getBounds()476 public Rectangle getBounds() 477 { 478 return table.getCellRect(row, column, true); 479 } 480 481 /** 482 * The bounds of the table cells cannot be manipulated directly, so 483 * this method does nothing. 484 * 485 * @param rectangle not used 486 */ setBounds(Rectangle rectangle)487 public void setBounds(Rectangle rectangle) 488 { 489 // This method does nothing. See API comments. 490 } 491 492 /** 493 * Returns the size of the table cell. 494 * 495 * @return the size of the table cell 496 */ getSize()497 public Dimension getSize() 498 { 499 Rectangle cellRect = table.getCellRect(row, column, true); 500 return new Dimension(cellRect.width, cellRect.height); 501 } 502 503 /** 504 * The size cannot be set on table cells directly, so this method does 505 * nothing. 506 * 507 * @param dimension not used 508 */ setSize(Dimension dimension)509 public void setSize(Dimension dimension) 510 { 511 // This method does nothing. See API comments. 512 } 513 514 /** 515 * Table cells have no children, so we return <code>null</code> here. 516 * 517 * @return <code>null</code> 518 */ getAccessibleAt(Point point)519 public Accessible getAccessibleAt(Point point) 520 { 521 return null; 522 } 523 524 /** 525 * Returns <code>true</code> if this table cell is focus traversable, 526 * <code>false</code> otherwise. 527 * 528 * @return <code>true</code> if this table cell is focus traversable, 529 * <code>false</code> otherwise 530 */ isFocusTraversable()531 public boolean isFocusTraversable() 532 { 533 return table.isFocusable(); 534 } 535 536 /** 537 * Requests that this table cell gets the keyboard focus. 538 */ requestFocus()539 public void requestFocus() 540 { 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(); 547 } 548 549 /** 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. 553 * 554 * @param listener the focus listener to add 555 */ addFocusListener(FocusListener listener)556 public void addFocusListener(FocusListener listener) 557 { 558 table.addFocusListener(listener); 559 } 560 561 /** 562 * Removes a focus listener from the cell. The focus listener is really 563 * removed from the table. 564 * 565 * @param listener the listener to remove 566 */ removeFocusListener(FocusListener listener)567 public void removeFocusListener(FocusListener listener) 568 { 569 table.removeFocusListener(listener); 570 } 571 572 } 573 574 protected class AccessibleJTableModelChange 575 implements AccessibleTableModelChange 576 { 577 protected int type; 578 protected int firstRow; 579 protected int lastRow; 580 protected int firstColumn; 581 protected int lastColumn; 582 AccessibleJTableModelChange(int type, int firstRow, int lastRow, int firstColumn, int lastColumn)583 protected AccessibleJTableModelChange(int type, int firstRow, 584 int lastRow, int firstColumn, 585 int lastColumn) 586 { 587 this.type = type; 588 this.firstRow = firstRow; 589 this.lastRow = lastRow; 590 this.firstColumn = firstColumn; 591 this.lastColumn = lastColumn; 592 } 593 getType()594 public int getType() 595 { 596 return type; 597 } 598 getFirstRow()599 public int getFirstRow() 600 { 601 return firstRow; 602 } 603 getLastRow()604 public int getLastRow() 605 { 606 return lastRow; 607 } 608 getFirstColumn()609 public int getFirstColumn() 610 { 611 return firstColumn; 612 } 613 getLastColumn()614 public int getLastColumn() 615 { 616 return lastColumn; 617 } 618 } 619 620 /** 621 * The RI returns an instance with this name in 622 * {@link #getAccessibleColumnHeader()}, this makes sense, so we do the 623 * same. 624 */ 625 private class AccessibleTableHeader 626 implements AccessibleTable 627 { 628 629 /** 630 * The JTableHeader wrapped by this class. 631 */ 632 private JTableHeader header; 633 634 /** 635 * Creates a new instance. 636 * 637 * @param h the JTableHeader to wrap 638 */ AccessibleTableHeader(JTableHeader h)639 private AccessibleTableHeader(JTableHeader h) 640 { 641 header = h; 642 } 643 644 /** 645 * Returns the caption for the table header. 646 * 647 * @return the caption for the table header 648 */ getAccessibleCaption()649 public Accessible getAccessibleCaption() 650 { 651 // The RI seems to always return null here, so do we. 652 return null; 653 } 654 655 /** 656 * Sets the caption for the table header. 657 * 658 * @param caption the caption to set 659 */ setAccessibleCaption(Accessible caption)660 public void setAccessibleCaption(Accessible caption) 661 { 662 // This seems to be a no-op in the RI, so we do the same. 663 } 664 665 /** 666 * Returns the caption for the table header. 667 * 668 * @return the caption for the table header 669 */ getAccessibleSummary()670 public Accessible getAccessibleSummary() 671 { 672 // The RI seems to always return null here, so do we. 673 return null; 674 } 675 676 /** 677 * Sets the summary for the table header. 678 * 679 * @param summary the caption to set 680 */ setAccessibleSummary(Accessible summary)681 public void setAccessibleSummary(Accessible summary) 682 { 683 // This seems to be a no-op in the RI, so we do the same. 684 } 685 686 /** 687 * Returns the number of rows, which is always 1 for the table header. 688 * 689 * @return the number of rows 690 */ getAccessibleRowCount()691 public int getAccessibleRowCount() 692 { 693 return 1; 694 } 695 696 /** 697 * Returns the number of columns in the table header. 698 * 699 * @return the number of columns in the table header 700 */ getAccessibleColumnCount()701 public int getAccessibleColumnCount() 702 { 703 return header.getColumnModel().getColumnCount(); 704 } 705 706 /** 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 710 * component. 711 * 712 * @param r the row number 713 * @param c the column number 714 * 715 * @return the accessible child at the specified row and column 716 */ getAccessibleAt(int r, int c)717 public Accessible getAccessibleAt(int r, int c) 718 { 719 TableColumn column = header.getColumnModel().getColumn(c); 720 TableCellRenderer rend = column.getHeaderRenderer(); 721 if (rend == null) 722 rend = header.getDefaultRenderer(); 723 Component comp = 724 rend.getTableCellRendererComponent(header.getTable(), 725 column.getHeaderValue(), false, 726 false, -1, c); 727 return new AccessibleJTableHeaderCell(header, comp, r, c); 728 } 729 getAccessibleRowExtentAt(int r, int c)730 public int getAccessibleRowExtentAt(int r, int c) 731 { 732 // TODO Auto-generated method stub 733 return 0; 734 } 735 getAccessibleColumnExtentAt(int r, int c)736 public int getAccessibleColumnExtentAt(int r, int c) 737 { 738 // TODO Auto-generated method stub 739 return 0; 740 } 741 getAccessibleRowHeader()742 public AccessibleTable getAccessibleRowHeader() 743 { 744 // TODO Auto-generated method stub 745 return null; 746 } 747 setAccessibleRowHeader(AccessibleTable header)748 public void setAccessibleRowHeader(AccessibleTable header) 749 { 750 // TODO Auto-generated method stub 751 752 } 753 getAccessibleColumnHeader()754 public AccessibleTable getAccessibleColumnHeader() 755 { 756 // TODO Auto-generated method stub 757 return null; 758 } 759 setAccessibleColumnHeader(AccessibleTable header)760 public void setAccessibleColumnHeader(AccessibleTable header) 761 { 762 // TODO Auto-generated method stub 763 764 } 765 getAccessibleRowDescription(int r)766 public Accessible getAccessibleRowDescription(int r) 767 { 768 // TODO Auto-generated method stub 769 return null; 770 } 771 setAccessibleRowDescription(int r, Accessible description)772 public void setAccessibleRowDescription(int r, Accessible description) 773 { 774 // TODO Auto-generated method stub 775 776 } 777 getAccessibleColumnDescription(int c)778 public Accessible getAccessibleColumnDescription(int c) 779 { 780 // TODO Auto-generated method stub 781 return null; 782 } 783 setAccessibleColumnDescription(int c, Accessible description)784 public void setAccessibleColumnDescription(int c, Accessible description) 785 { 786 // TODO Auto-generated method stub 787 788 } 789 isAccessibleSelected(int r, int c)790 public boolean isAccessibleSelected(int r, int c) 791 { 792 // TODO Auto-generated method stub 793 return false; 794 } 795 isAccessibleRowSelected(int r)796 public boolean isAccessibleRowSelected(int r) 797 { 798 // TODO Auto-generated method stub 799 return false; 800 } 801 isAccessibleColumnSelected(int c)802 public boolean isAccessibleColumnSelected(int c) 803 { 804 // TODO Auto-generated method stub 805 return false; 806 } 807 getSelectedAccessibleRows()808 public int[] getSelectedAccessibleRows() 809 { 810 // TODO Auto-generated method stub 811 return null; 812 } 813 getSelectedAccessibleColumns()814 public int[] getSelectedAccessibleColumns() 815 { 816 // TODO Auto-generated method stub 817 return null; 818 } 819 820 } 821 822 /** 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. 826 */ 827 private class AccessibleJTableHeaderCell 828 extends AccessibleContext 829 implements Accessible, AccessibleComponent 830 { 831 832 JTableHeader header; 833 834 int columnIndex; 835 836 /** 837 * 838 * @param h the table header. 839 * @param comp 840 * @param r 841 * @param c the column index. 842 */ AccessibleJTableHeaderCell(JTableHeader h, Component comp, int r, int c)843 private AccessibleJTableHeaderCell(JTableHeader h, Component comp, int r, 844 int c) 845 { 846 header = h; 847 columnIndex = c; 848 } 849 850 /** 851 * Returns the header renderer. 852 * 853 * @return The header renderer. 854 */ getColumnHeaderRenderer()855 Component getColumnHeaderRenderer() 856 { 857 TableColumn tc = header.getColumnModel().getColumn(columnIndex); 858 TableCellRenderer r = tc.getHeaderRenderer(); 859 if (r == null) 860 r = header.getDefaultRenderer(); 861 return r.getTableCellRendererComponent(header.getTable(), 862 tc.getHeaderValue(), false, false, -1, columnIndex); 863 } 864 865 /** 866 * Returns the accessible role for the table header cell. 867 * 868 * @return The accessible role. 869 */ getAccessibleRole()870 public AccessibleRole getAccessibleRole() 871 { 872 Component renderer = getColumnHeaderRenderer(); 873 if (renderer instanceof Accessible) 874 { 875 Accessible ac = (Accessible) renderer; 876 return ac.getAccessibleContext().getAccessibleRole(); 877 } 878 return null; 879 } 880 getAccessibleStateSet()881 public AccessibleStateSet getAccessibleStateSet() 882 { 883 // TODO Auto-generated method stub 884 return null; 885 } 886 getAccessibleIndexInParent()887 public int getAccessibleIndexInParent() 888 { 889 // TODO Auto-generated method stub 890 return 0; 891 } 892 getAccessibleChildrenCount()893 public int getAccessibleChildrenCount() 894 { 895 // TODO Auto-generated method stub 896 return 0; 897 } 898 getAccessibleChild(int i)899 public Accessible getAccessibleChild(int i) 900 { 901 // TODO Auto-generated method stub 902 return null; 903 } 904 getLocale()905 public Locale getLocale() 906 { 907 // TODO Auto-generated method stub 908 return null; 909 } 910 911 /** 912 * Returns the accessible context. 913 * 914 * @return <code>this</code>. 915 */ getAccessibleContext()916 public AccessibleContext getAccessibleContext() 917 { 918 return this; 919 } 920 getBackground()921 public Color getBackground() 922 { 923 // TODO Auto-generated method stub 924 return null; 925 } 926 setBackground(Color color)927 public void setBackground(Color color) 928 { 929 // TODO Auto-generated method stub 930 931 } 932 getForeground()933 public Color getForeground() 934 { 935 // TODO Auto-generated method stub 936 return null; 937 } 938 setForeground(Color color)939 public void setForeground(Color color) 940 { 941 // TODO Auto-generated method stub 942 943 } 944 getCursor()945 public Cursor getCursor() 946 { 947 // TODO Auto-generated method stub 948 return null; 949 } 950 setCursor(Cursor cursor)951 public void setCursor(Cursor cursor) 952 { 953 // TODO Auto-generated method stub 954 955 } 956 getFont()957 public Font getFont() 958 { 959 // TODO Auto-generated method stub 960 return null; 961 } 962 setFont(Font font)963 public void setFont(Font font) 964 { 965 // TODO Auto-generated method stub 966 967 } 968 getFontMetrics(Font font)969 public FontMetrics getFontMetrics(Font font) 970 { 971 // TODO Auto-generated method stub 972 return null; 973 } 974 isEnabled()975 public boolean isEnabled() 976 { 977 // TODO Auto-generated method stub 978 return false; 979 } 980 setEnabled(boolean b)981 public void setEnabled(boolean b) 982 { 983 // TODO Auto-generated method stub 984 985 } 986 isVisible()987 public boolean isVisible() 988 { 989 // TODO Auto-generated method stub 990 return false; 991 } 992 setVisible(boolean b)993 public void setVisible(boolean b) 994 { 995 // TODO Auto-generated method stub 996 997 } 998 isShowing()999 public boolean isShowing() 1000 { 1001 // TODO Auto-generated method stub 1002 return false; 1003 } 1004 contains(Point point)1005 public boolean contains(Point point) 1006 { 1007 // TODO Auto-generated method stub 1008 return false; 1009 } 1010 getLocationOnScreen()1011 public Point getLocationOnScreen() 1012 { 1013 // TODO Auto-generated method stub 1014 return null; 1015 } 1016 getLocation()1017 public Point getLocation() 1018 { 1019 // TODO Auto-generated method stub 1020 return null; 1021 } 1022 setLocation(Point point)1023 public void setLocation(Point point) 1024 { 1025 // TODO Auto-generated method stub 1026 1027 } 1028 getBounds()1029 public Rectangle getBounds() 1030 { 1031 // TODO Auto-generated method stub 1032 return null; 1033 } 1034 setBounds(Rectangle rectangle)1035 public void setBounds(Rectangle rectangle) 1036 { 1037 // TODO Auto-generated method stub 1038 1039 } 1040 getSize()1041 public Dimension getSize() 1042 { 1043 // TODO Auto-generated method stub 1044 return null; 1045 } 1046 setSize(Dimension dimension)1047 public void setSize(Dimension dimension) 1048 { 1049 // TODO Auto-generated method stub 1050 1051 } 1052 getAccessibleAt(Point point)1053 public Accessible getAccessibleAt(Point point) 1054 { 1055 // TODO Auto-generated method stub 1056 return null; 1057 } 1058 isFocusTraversable()1059 public boolean isFocusTraversable() 1060 { 1061 // TODO Auto-generated method stub 1062 return false; 1063 } 1064 requestFocus()1065 public void requestFocus() 1066 { 1067 // TODO Auto-generated method stub 1068 1069 } 1070 addFocusListener(FocusListener listener)1071 public void addFocusListener(FocusListener listener) 1072 { 1073 // TODO Auto-generated method stub 1074 1075 } 1076 removeFocusListener(FocusListener listener)1077 public void removeFocusListener(FocusListener listener) 1078 { 1079 // TODO Auto-generated method stub 1080 1081 } 1082 1083 } 1084 1085 /** 1086 * The last selected row. This is needed to track the selection in 1087 * {@link #valueChanged(ListSelectionEvent)}. 1088 */ 1089 private int lastSelectedRow; 1090 1091 /** 1092 * The last selected column. This is needed to track the selection in 1093 * {@link #valueChanged(ListSelectionEvent)}. 1094 */ 1095 private int lastSelectedColumn; 1096 1097 /** 1098 * The caption of the table. 1099 */ 1100 private Accessible caption; 1101 1102 /** 1103 * The summary of the table. 1104 */ 1105 private Accessible summary; 1106 1107 /** 1108 * Accessible descriptions for rows. 1109 */ 1110 private Accessible[] rowDescriptions; 1111 1112 /** 1113 * Accessible descriptions for columns. 1114 */ 1115 private Accessible[] columnDescriptions; 1116 1117 /** 1118 * Creates a new <code>AccessibleJTable</code>. 1119 * 1120 * @since JDK1.5 1121 */ AccessibleJTable()1122 protected AccessibleJTable() 1123 { 1124 getModel().addTableModelListener(this); 1125 getSelectionModel().addListSelectionListener(this); 1126 getColumnModel().addColumnModelListener(this); 1127 lastSelectedRow = getSelectedRow(); 1128 lastSelectedColumn = getSelectedColumn(); 1129 TableCellEditor editor = getCellEditor(); 1130 if (editor != null) 1131 editor.addCellEditorListener(this); 1132 } 1133 1134 /** 1135 * Returns the accessible role for the <code>JTable</code> component. 1136 * 1137 * @return {@link AccessibleRole#TABLE}. 1138 */ getAccessibleRole()1139 public AccessibleRole getAccessibleRole() 1140 { 1141 return AccessibleRole.TABLE; 1142 } 1143 1144 /** 1145 * Returns the accessible table. 1146 * 1147 * @return <code>this</code>. 1148 */ getAccessibleTable()1149 public AccessibleTable getAccessibleTable() 1150 { 1151 return this; 1152 } 1153 1154 /** 1155 * Returns the number of selected items in this table. 1156 */ getAccessibleSelectionCount()1157 public int getAccessibleSelectionCount() 1158 { 1159 return getSelectedColumnCount(); 1160 } 1161 1162 /** 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. 1166 * 1167 * @param i the index of the selected object to find 1168 * 1169 * @return the selected accessible object with the specified index 1170 * <code>i</code> 1171 */ getAccessibleSelection(int i)1172 public Accessible getAccessibleSelection(int i) 1173 { 1174 Accessible found = null; 1175 1176 int[] selectedRows = getSelectedRows(); 1177 int[] selectedColumns = getSelectedColumns(); 1178 int numCols = getColumnCount(); 1179 int numRows = getRowCount(); 1180 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()) 1185 { 1186 int current = -1; 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++) 1192 { 1193 // Handle unselected rows between this selected and the last 1194 // selected row, if any. 1195 int selectedRow = selectedRows[j]; 1196 int r = -1; 1197 int ci = -1; 1198 for (r = lastSelectedRow + 1; 1199 r < selectedRow && current < i; r++) 1200 { 1201 for (ci = 0; ci < selectedColumns.length && current < i; 1202 ci++) 1203 { 1204 current++; 1205 } 1206 } 1207 if (current == i) 1208 { 1209 // We found the cell in the above loops, now get out of here. 1210 found = getAccessibleChild(r * numCols 1211 + selectedColumns[ci]); 1212 break; 1213 } 1214 1215 // If we're still here, handle the current selected row. 1216 if (current < i && current + numCols >= i) 1217 { 1218 // The cell must be in that row, which one is it? 1219 found = getAccessibleChild(r * numCols + (i - current)); 1220 break; 1221 } 1222 current += numCols; 1223 } 1224 if (found == null) 1225 { 1226 // The cell can still be in the last couple of unselected rows. 1227 int r = 0; 1228 int ci = 0; 1229 for (r = lastSelectedRow + 1; 1230 r < numRows && current < i; r++) 1231 { 1232 for (ci = 0; ci < selectedColumns.length && current < i; 1233 ci++) 1234 { 1235 current++; 1236 } 1237 } 1238 if (current == i) 1239 { 1240 // We found the cell in the above loops, now get out of here. 1241 found = getAccessibleChild(r * numCols 1242 + selectedColumns[ci]); 1243 } 1244 } 1245 } 1246 // One or more rows can be completely selected. 1247 else if (getRowSelectionAllowed()) 1248 { 1249 int c = i % numCols; 1250 int r = selectedRows[i / numCols]; 1251 found = getAccessibleChild(r * numCols + c); 1252 } 1253 // One or more columns can be completely selected. 1254 else if (getRowSelectionAllowed()) 1255 { 1256 int numSelectedColumns = selectedColumns.length; 1257 int c = selectedColumns[i % numSelectedColumns]; 1258 int r = i / numSelectedColumns; 1259 found = getAccessibleChild(r * numCols + c); 1260 } 1261 1262 return found; 1263 } 1264 1265 /** 1266 * Returns <code>true</code> if the accessible child with the index 1267 * <code>i</code> is selected, <code>false</code> otherwise. 1268 * 1269 * @param i the index of the accessible to check 1270 * 1271 * @return <code>true</code> if the accessible child with the index 1272 * <code>i</code> is selected, <code>false</code> otherwise 1273 */ isAccessibleChildSelected(int i)1274 public boolean isAccessibleChildSelected(int i) 1275 { 1276 int r = getAccessibleRowAtIndex(i); 1277 int c = getAccessibleColumnAtIndex(i); 1278 return isCellSelected(r, c); 1279 } 1280 1281 /** 1282 * Adds the accessible child with the specified index <code>i</code> to the 1283 * selection. 1284 * 1285 * @param i the index of the accessible child to add to the selection 1286 */ addAccessibleSelection(int i)1287 public void addAccessibleSelection(int i) 1288 { 1289 int r = getAccessibleRowAtIndex(i); 1290 int c = getAccessibleColumnAtIndex(i); 1291 changeSelection(r, c, true, false); 1292 } 1293 1294 /** 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>). 1299 * 1300 * @param i the index of the accessible to be removed from the selection 1301 */ removeAccessibleSelection(int i)1302 public void removeAccessibleSelection(int i) 1303 { 1304 if (! getRowSelectionAllowed() && ! getColumnSelectionAllowed()) 1305 { 1306 int r = getAccessibleRowAtIndex(i); 1307 int c = getAccessibleColumnAtIndex(i); 1308 removeRowSelectionInterval(r, r); 1309 removeColumnSelectionInterval(c, c); 1310 } 1311 } 1312 1313 /** 1314 * Deselects all selected accessible children. 1315 */ clearAccessibleSelection()1316 public void clearAccessibleSelection() 1317 { 1318 clearSelection(); 1319 } 1320 1321 /** 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. 1325 */ selectAllAccessibleSelection()1326 public void selectAllAccessibleSelection() 1327 { 1328 selectAll(); 1329 } 1330 1331 /** 1332 * Receives notification when the row selection changes and fires 1333 * appropriate property change events. 1334 * 1335 * @param event the list selection event 1336 */ valueChanged(ListSelectionEvent event)1337 public void valueChanged(ListSelectionEvent event) 1338 { 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) 1344 { 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; 1352 } 1353 } 1354 1355 /** 1356 * Receives notification when the table model changes. Depending on the 1357 * type of change, this method calls {@link #tableRowsInserted} or 1358 * {@link #tableRowsDeleted}. 1359 * 1360 * @param event the table model event 1361 */ tableChanged(TableModelEvent event)1362 public void tableChanged(TableModelEvent event) 1363 { 1364 switch (event.getType()) 1365 { 1366 case TableModelEvent.INSERT: 1367 tableRowsInserted(event); 1368 break; 1369 case TableModelEvent.DELETE: 1370 tableRowsDeleted(event); 1371 break; 1372 } 1373 } 1374 1375 /** 1376 * Receives notification when one or more rows have been inserted into the 1377 * table and fires appropriate property change events. 1378 * 1379 * @param event the table model event 1380 */ tableRowsInserted(TableModelEvent event)1381 public void tableRowsInserted(TableModelEvent event) 1382 { 1383 handleRowChange(event); 1384 } 1385 1386 /** 1387 * Receives notification when one or more rows have been deleted from the 1388 * table. 1389 * 1390 * @param event the table model event 1391 */ tableRowsDeleted(TableModelEvent event)1392 public void tableRowsDeleted(TableModelEvent event) 1393 { 1394 handleRowChange(event); 1395 } 1396 1397 /** 1398 * Fires a PropertyChangeEvent for inserted or deleted rows. 1399 * 1400 * @param event the table model event 1401 */ handleRowChange(TableModelEvent event)1402 private void handleRowChange(TableModelEvent event) 1403 { 1404 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 1405 null, null); 1406 int firstColumn = event.getColumn(); 1407 int lastColumn = event.getColumn(); 1408 if (firstColumn == TableModelEvent.ALL_COLUMNS) 1409 { 1410 firstColumn = 0; 1411 lastColumn = getColumnCount() - 1; 1412 } 1413 AccessibleJTableModelChange change = new AccessibleJTableModelChange 1414 (event.getType(), event.getFirstRow(), event.getLastRow(), 1415 firstColumn, lastColumn); 1416 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED, 1417 null, change); 1418 } 1419 columnAdded(TableColumnModelEvent event)1420 public void columnAdded(TableColumnModelEvent event) 1421 { 1422 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 1423 null, null); 1424 handleColumnChange(AccessibleTableModelChange.INSERT, 1425 event.getFromIndex(), event.getToIndex()); 1426 } 1427 columnRemoved(TableColumnModelEvent event)1428 public void columnRemoved(TableColumnModelEvent event) 1429 { 1430 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 1431 null, null); 1432 handleColumnChange(AccessibleTableModelChange.DELETE, 1433 event.getFromIndex(), event.getToIndex()); 1434 } 1435 columnMoved(TableColumnModelEvent event)1436 public void columnMoved(TableColumnModelEvent event) 1437 { 1438 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 1439 null, null); 1440 handleColumnChange(AccessibleTableModelChange.DELETE, 1441 event.getFromIndex(), event.getFromIndex()); 1442 handleColumnChange(AccessibleTableModelChange.INSERT, 1443 event.getFromIndex(), event.getToIndex()); 1444 } 1445 1446 /** 1447 * Fires a PropertyChangeEvent for inserted or deleted columns. 1448 * 1449 * @param type the type of change 1450 * @param from the start of the change 1451 * @param to the target of the change 1452 */ handleColumnChange(int type, int from, int to)1453 private void handleColumnChange(int type, int from, int to) 1454 { 1455 AccessibleJTableModelChange change = 1456 new AccessibleJTableModelChange(type, 0, 0, from, to); 1457 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED, 1458 null, change); 1459 } 1460 columnMarginChanged(ChangeEvent event)1461 public void columnMarginChanged(ChangeEvent event) 1462 { 1463 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 1464 null, null); 1465 } 1466 columnSelectionChanged(ListSelectionEvent event)1467 public void columnSelectionChanged(ListSelectionEvent event) 1468 { 1469 // AFAICS, nothing is done here. 1470 } 1471 editingCanceled(ChangeEvent event)1472 public void editingCanceled(ChangeEvent event) 1473 { 1474 // AFAICS, nothing is done here. 1475 } 1476 editingStopped(ChangeEvent event)1477 public void editingStopped(ChangeEvent event) 1478 { 1479 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 1480 null, null); 1481 } 1482 1483 /** 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. 1487 * 1488 * @param e the property change event 1489 */ propertyChange(PropertyChangeEvent e)1490 public void propertyChange(PropertyChangeEvent e) 1491 { 1492 String propName = e.getPropertyName(); 1493 if (propName.equals("tableModel")) 1494 { 1495 TableModel oldModel = (TableModel) e.getOldValue(); 1496 oldModel.removeTableModelListener(this); 1497 TableModel newModel = (TableModel) e.getNewValue(); 1498 newModel.addTableModelListener(this); 1499 } 1500 else if (propName.equals("columnModel")) 1501 { 1502 TableColumnModel oldModel = (TableColumnModel) e.getOldValue(); 1503 oldModel.removeColumnModelListener(this); 1504 TableColumnModel newModel = (TableColumnModel) e.getNewValue(); 1505 newModel.addColumnModelListener(this); 1506 } 1507 else if (propName.equals("selectionModel")) 1508 { 1509 ListSelectionModel oldModel = (ListSelectionModel) e.getOldValue(); 1510 oldModel.removeListSelectionListener(this); 1511 ListSelectionModel newModel = (ListSelectionModel) e.getNewValue(); 1512 newModel.addListSelectionListener(this); 1513 } 1514 else if (propName.equals("cellEditor")) 1515 { 1516 CellEditor oldEd = (CellEditor) e.getOldValue(); 1517 oldEd.removeCellEditorListener(this); 1518 CellEditor newEd = (CellEditor) e.getNewValue(); 1519 newEd.addCellEditorListener(this); 1520 } 1521 } 1522 1523 /** 1524 * Returns the row number of an accessible child (cell) with the specified 1525 * index. 1526 * 1527 * @param index the index of the cell of which the row number is queried 1528 * 1529 * @return the row number of an accessible child (cell) with the specified 1530 * index 1531 */ getAccessibleRow(int index)1532 public int getAccessibleRow(int index) 1533 { 1534 return getAccessibleRowAtIndex(index); 1535 } 1536 1537 /** 1538 * Returns the column number of an accessible child (cell) with the 1539 * specified index. 1540 * 1541 * @param index the index of the cell of which the column number is queried 1542 * 1543 * @return the column number of an accessible child (cell) with the 1544 * specified index 1545 */ getAccessibleColumn(int index)1546 public int getAccessibleColumn(int index) 1547 { 1548 return getAccessibleColumnAtIndex(index); 1549 } 1550 1551 /** 1552 * Returns the index of the accessible child at the specified row and 1553 * column. 1554 * 1555 * @param r the row number 1556 * @param c the column number 1557 * 1558 * @return the index of the accessible child at the specified row and 1559 * column 1560 */ getAccessibleIndex(int r, int c)1561 public int getAccessibleIndex(int r, int c) 1562 { 1563 return getAccessibleIndexAt(r, c); 1564 } 1565 1566 /** 1567 * Returns the caption of the table. 1568 * 1569 * @return the caption of the table 1570 * 1571 * @see #setAccessibleCaption(Accessible) 1572 */ getAccessibleCaption()1573 public Accessible getAccessibleCaption() 1574 { 1575 return caption; 1576 } 1577 1578 /** 1579 * Sets the caption for the table. 1580 * 1581 * @param c the caption to set 1582 */ setAccessibleCaption(Accessible c)1583 public void setAccessibleCaption(Accessible c) 1584 { 1585 caption = c; 1586 } 1587 1588 /** 1589 * Returns the summary for the table. 1590 * 1591 * @return the summary for the table 1592 */ getAccessibleSummary()1593 public Accessible getAccessibleSummary() 1594 { 1595 return summary; 1596 } 1597 1598 /** 1599 * Sets the summary for the table. 1600 * 1601 * @param s the summary to set 1602 */ setAccessibleSummary(Accessible s)1603 public void setAccessibleSummary(Accessible s) 1604 { 1605 summary = s; 1606 } 1607 1608 /** 1609 * Returns the number of rows in the table. 1610 * 1611 * @return the number of rows in the table 1612 */ getAccessibleRowCount()1613 public int getAccessibleRowCount() 1614 { 1615 return getRowCount(); 1616 } 1617 1618 /** 1619 * Returns the number of columns in the table. 1620 * 1621 * @return the number of columns in the table 1622 */ getAccessibleColumnCount()1623 public int getAccessibleColumnCount() 1624 { 1625 return getColumnCount(); 1626 } 1627 1628 /** 1629 * Returns the accessible child at the given index. 1630 * 1631 * @param index the child index. 1632 * 1633 * @return The accessible child. 1634 */ getAccessibleChild(int index)1635 public Accessible getAccessibleChild(int index) 1636 { 1637 int r = getAccessibleRow(index); 1638 int c = getAccessibleColumn(index); 1639 return getAccessibleAt(r, c); 1640 } 1641 1642 /** 1643 * Returns the accessible child (table cell) at the specified row and 1644 * column. 1645 * 1646 * @param r the row number 1647 * @param c the column number 1648 * 1649 * @return the accessible child (table cell) at the specified row and 1650 * column 1651 */ getAccessibleAt(int r, int c)1652 public Accessible getAccessibleAt(int r, int c) 1653 { 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; 1659 return null; 1660 } 1661 1662 /** 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> 1665 * here. 1666 * 1667 * @param r the row number 1668 * @param c the column number 1669 * 1670 * @return the number of rows that the specified cell occupies 1671 */ getAccessibleRowExtentAt(int r, int c)1672 public int getAccessibleRowExtentAt(int r, int c) 1673 { 1674 return 1; 1675 } 1676 1677 /** 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> 1680 * here. 1681 * 1682 * @param r the row number 1683 * @param c the column number 1684 * 1685 * @return the number of rows that the specified cell occupies 1686 */ getAccessibleColumnExtentAt(int r, int c)1687 public int getAccessibleColumnExtentAt(int r, int c) 1688 { 1689 return 1; 1690 } 1691 1692 /** 1693 * Returns the accessible row header. 1694 * 1695 * @return the accessible row header 1696 */ getAccessibleRowHeader()1697 public AccessibleTable getAccessibleRowHeader() 1698 { 1699 // The RI seems to always return null here, so do we. 1700 return null; 1701 } 1702 1703 /** 1704 * Sets the accessible row header. 1705 * 1706 * @param header the header to set 1707 */ setAccessibleRowHeader(AccessibleTable header)1708 public void setAccessibleRowHeader(AccessibleTable header) 1709 { 1710 // In the RI this seems to be a no-op. 1711 } 1712 1713 /** 1714 * Returns the column header. 1715 * 1716 * @return the column header, or <code>null</code> if there is no column 1717 * header 1718 */ getAccessibleColumnHeader()1719 public AccessibleTable getAccessibleColumnHeader() 1720 { 1721 JTableHeader h = getTableHeader(); 1722 AccessibleTable header = null; 1723 if (h != null) 1724 header = new AccessibleTableHeader(h); 1725 return header; 1726 } 1727 1728 /** 1729 * Sets the accessible column header. The default implementation doesn't 1730 * allow changing the header this way, so this is a no-op. 1731 * 1732 * @param header the accessible column header to set 1733 */ setAccessibleColumnHeader(AccessibleTable header)1734 public void setAccessibleColumnHeader(AccessibleTable header) 1735 { 1736 // The RI doesn't seem to do anything, so we also do nothing. 1737 } 1738 1739 /** 1740 * Returns the accessible description for the row with the specified index, 1741 * or <code>null</code> if no description has been set. 1742 * 1743 * @param r the row for which the description is queried 1744 * 1745 * @return the accessible description for the row with the specified index, 1746 * or <code>null</code> if no description has been set 1747 */ getAccessibleRowDescription(int r)1748 public Accessible getAccessibleRowDescription(int r) 1749 { 1750 Accessible descr = null; 1751 if (rowDescriptions != null) 1752 descr = rowDescriptions[r]; 1753 return descr; 1754 } 1755 1756 /** 1757 * Sets the accessible description for the row with the specified index. 1758 * 1759 * @param r the row number for which to set the description 1760 * @param description the description to set 1761 */ setAccessibleRowDescription(int r, Accessible description)1762 public void setAccessibleRowDescription(int r, Accessible description) 1763 { 1764 if (rowDescriptions == null) 1765 rowDescriptions = new Accessible[getAccessibleRowCount()]; 1766 rowDescriptions[r] = description; 1767 } 1768 1769 /** 1770 * Returns the accessible description for the column with the specified 1771 * index, or <code>null</code> if no description has been set. 1772 * 1773 * @param c the column for which the description is queried 1774 * 1775 * @return the accessible description for the column with the specified 1776 * index, or <code>null</code> if no description has been set 1777 */ getAccessibleColumnDescription(int c)1778 public Accessible getAccessibleColumnDescription(int c) 1779 { 1780 Accessible descr = null; 1781 if (columnDescriptions != null) 1782 descr = columnDescriptions[c]; 1783 return descr; 1784 } 1785 1786 /** 1787 * Sets the accessible description for the column with the specified index. 1788 * 1789 * @param c the column number for which to set the description 1790 * @param description the description to set 1791 */ setAccessibleColumnDescription(int c, Accessible description)1792 public void setAccessibleColumnDescription(int c, Accessible description) 1793 { 1794 if (columnDescriptions == null) 1795 columnDescriptions = new Accessible[getAccessibleRowCount()]; 1796 columnDescriptions[c] = description; 1797 } 1798 1799 /** 1800 * Returns <code>true</code> if the accessible child at the specified 1801 * row and column is selected, <code>false</code> otherwise. 1802 * 1803 * @param r the row number of the child 1804 * @param c the column number of the child 1805 * 1806 * @return <code>true</code> if the accessible child at the specified 1807 * row and column is selected, <code>false</code> otherwise 1808 */ isAccessibleSelected(int r, int c)1809 public boolean isAccessibleSelected(int r, int c) 1810 { 1811 return isCellSelected(r, c); 1812 } 1813 1814 /** 1815 * Returns <code>true</code> if the row with the specified index is 1816 * selected, <code>false</code> otherwise. 1817 * 1818 * @param r the row number 1819 * 1820 * @return <code>true</code> if the row with the specified index is 1821 * selected, <code>false</code> otherwise 1822 */ isAccessibleRowSelected(int r)1823 public boolean isAccessibleRowSelected(int r) 1824 { 1825 return isRowSelected(r); 1826 } 1827 1828 /** 1829 * Returns <code>true</code> if the column with the specified index is 1830 * selected, <code>false</code> otherwise. 1831 * 1832 * @param c the column number 1833 * 1834 * @return <code>true</code> if the column with the specified index is 1835 * selected, <code>false</code> otherwise 1836 */ isAccessibleColumnSelected(int c)1837 public boolean isAccessibleColumnSelected(int c) 1838 { 1839 return isColumnSelected(c); 1840 } 1841 1842 /** 1843 * Returns the indices of all selected rows. 1844 * 1845 * @return the indices of all selected rows 1846 */ getSelectedAccessibleRows()1847 public int[] getSelectedAccessibleRows() 1848 { 1849 return getSelectedRows(); 1850 } 1851 1852 /** 1853 * Returns the indices of all selected columns. 1854 * 1855 * @return the indices of all selected columns 1856 */ getSelectedAccessibleColumns()1857 public int[] getSelectedAccessibleColumns() 1858 { 1859 return getSelectedColumns(); 1860 } 1861 1862 /** 1863 * Returns the accessible row at the specified index. 1864 * 1865 * @param index the index for which to query the row 1866 * 1867 * @return the row number at the specified table index 1868 */ getAccessibleRowAtIndex(int index)1869 public int getAccessibleRowAtIndex(int index) 1870 { 1871 // TODO: Back this up by a Mauve test and update API docs accordingly. 1872 return index / getColumnCount(); 1873 } 1874 1875 /** 1876 * Returns the accessible column at the specified index. 1877 * 1878 * @param index the index for which to query the column 1879 * 1880 * @return the column number at the specified table index 1881 */ getAccessibleColumnAtIndex(int index)1882 public int getAccessibleColumnAtIndex(int index) 1883 { 1884 // TODO: Back this up by a Mauve test and update API docs accordingly. 1885 return index % getColumnCount(); 1886 } 1887 1888 /** 1889 * Returns the accessible child index at the specified column and row. 1890 * 1891 * @param row the row 1892 * @param column the column 1893 * 1894 * @return the index of the accessible child at the specified row and 1895 * column 1896 */ getAccessibleIndexAt(int row, int column)1897 public int getAccessibleIndexAt(int row, int column) 1898 { 1899 // TODO: Back this up by a Mauve test and update API docs accordingly. 1900 return row * getColumnCount() + column; 1901 } 1902 } 1903 /** 1904 * Handles property changes from the <code>TableColumn</code>s of this 1905 * <code>JTable</code>. 1906 * 1907 * More specifically, this triggers a {@link #revalidate()} call if the 1908 * preferredWidth of one of the observed columns changes. 1909 */ 1910 class TableColumnPropertyChangeHandler implements PropertyChangeListener 1911 { 1912 /** 1913 * Receives notification that a property of the observed TableColumns has 1914 * changed. 1915 * 1916 * @param ev the property change event 1917 */ propertyChange(PropertyChangeEvent ev)1918 public void propertyChange(PropertyChangeEvent ev) 1919 { 1920 if (ev.getPropertyName().equals("preferredWidth")) 1921 { 1922 JTableHeader header = getTableHeader(); 1923 if (header != null) 1924 // Do nothing if the table is in the resizing mode. 1925 if (header.getResizingColumn() == null) 1926 { 1927 TableColumn col = (TableColumn) ev.getSource(); 1928 header.setResizingColumn(col); 1929 doLayout(); 1930 header.setResizingColumn(null); 1931 } 1932 } 1933 } 1934 } 1935 1936 /** 1937 * A cell renderer for boolean values. 1938 */ 1939 private class BooleanCellRenderer 1940 extends DefaultTableCellRenderer 1941 { 1942 /** 1943 * The CheckBox that is used for rendering. 1944 */ 1945 private final JCheckBox checkBox; 1946 1947 /** 1948 * Creates a new checkbox based boolean cell renderer. The checkbox is 1949 * centered by default. 1950 */ BooleanCellRenderer()1951 BooleanCellRenderer() 1952 { 1953 checkBox = new JCheckBox(); 1954 checkBox.setHorizontalAlignment(SwingConstants.CENTER); 1955 } 1956 1957 /** 1958 * Get the check box. 1959 */ getCheckBox()1960 JCheckBox getCheckBox() 1961 { 1962 return checkBox; 1963 } 1964 1965 /** 1966 * Returns the component that is used for rendering the value. 1967 * 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) 1975 */ getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)1976 public Component getTableCellRendererComponent(JTable table, Object value, 1977 boolean isSelected, 1978 boolean hasFocus, int row, 1979 int column) 1980 { 1981 if (isSelected) 1982 { 1983 checkBox.setBackground(table.getSelectionBackground()); 1984 checkBox.setForeground(table.getSelectionForeground()); 1985 } 1986 else 1987 { 1988 checkBox.setBackground(table.getBackground()); 1989 checkBox.setForeground(table.getForeground()); 1990 } 1991 1992 if (hasFocus) 1993 { 1994 checkBox.setBorder( 1995 UIManager.getBorder("Table.focusCellHighlightBorder")); 1996 if (table.isCellEditable(row, column)) 1997 { 1998 checkBox.setBackground( 1999 UIManager.getColor("Table.focusCellBackground")); 2000 checkBox.setForeground( 2001 UIManager.getColor("Table.focusCellForeground")); 2002 } 2003 } 2004 else 2005 checkBox.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1)); 2006 2007 // Null is rendered as false. 2008 if (value == null) 2009 checkBox.setSelected(false); 2010 else 2011 { 2012 Boolean boolValue = (Boolean) value; 2013 checkBox.setSelected(boolValue.booleanValue()); 2014 } 2015 return checkBox; 2016 } 2017 } 2018 2019 /** 2020 * A cell renderer for Date values. 2021 */ 2022 private class DateCellRenderer 2023 extends DefaultTableCellRenderer 2024 { 2025 /** 2026 * Returns the component that is used for rendering the value. 2027 * 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 2034 * 2035 * @return this component (the default table cell renderer) 2036 */ getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)2037 public Component getTableCellRendererComponent(JTable table, Object value, 2038 boolean isSelected, 2039 boolean hasFocus, int row, 2040 int column) 2041 { 2042 super.getTableCellRendererComponent(table, value, isSelected, hasFocus, 2043 row, column); 2044 if (value instanceof Date) 2045 { 2046 Date dateValue = (Date) value; 2047 DateFormat df = DateFormat.getDateInstance(DateFormat.SHORT); 2048 setText(df.format(dateValue)); 2049 } 2050 return this; 2051 } 2052 } 2053 2054 /** 2055 * A cell renderer for Double values. 2056 */ 2057 private class DoubleCellRenderer 2058 extends DefaultTableCellRenderer 2059 { 2060 /** 2061 * Creates a new instance of NumberCellRenderer. 2062 */ DoubleCellRenderer()2063 public DoubleCellRenderer() 2064 { 2065 setHorizontalAlignment(JLabel.RIGHT); 2066 } 2067 2068 /** 2069 * Returns the component that is used for rendering the value. 2070 * 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 2077 * 2078 * @return this component (the default table cell renderer) 2079 */ getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)2080 public Component getTableCellRendererComponent(JTable table, Object value, 2081 boolean isSelected, 2082 boolean hasFocus, int row, 2083 int column) 2084 { 2085 super.getTableCellRendererComponent(table, value, isSelected, hasFocus, 2086 row, column); 2087 if (value instanceof Double) 2088 { 2089 Double doubleValue = (Double) value; 2090 NumberFormat nf = NumberFormat.getInstance(); 2091 setText(nf.format(doubleValue.doubleValue())); 2092 } 2093 return this; 2094 } 2095 } 2096 2097 /** 2098 * A cell renderer for Float values. 2099 */ 2100 private class FloatCellRenderer 2101 extends DefaultTableCellRenderer 2102 { 2103 /** 2104 * Creates a new instance of NumberCellRenderer. 2105 */ FloatCellRenderer()2106 public FloatCellRenderer() 2107 { 2108 setHorizontalAlignment(JLabel.RIGHT); 2109 } 2110 2111 /** 2112 * Returns the component that is used for rendering the value. 2113 * 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 2120 * 2121 * @return this component (the default table cell renderer) 2122 */ getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)2123 public Component getTableCellRendererComponent(JTable table, Object value, 2124 boolean isSelected, 2125 boolean hasFocus, int row, 2126 int column) 2127 { 2128 super.getTableCellRendererComponent(table, value, isSelected, hasFocus, 2129 row, column); 2130 if (value instanceof Float) 2131 { 2132 Float floatValue = (Float) value; 2133 NumberFormat nf = NumberFormat.getInstance(); 2134 setText(nf.format(floatValue.floatValue())); 2135 } 2136 return this; 2137 } 2138 } 2139 2140 /** 2141 * A cell renderer for Number values. 2142 */ 2143 private class NumberCellRenderer 2144 extends DefaultTableCellRenderer 2145 { 2146 /** 2147 * Creates a new instance of NumberCellRenderer. 2148 */ NumberCellRenderer()2149 public NumberCellRenderer() 2150 { 2151 setHorizontalAlignment(JLabel.RIGHT); 2152 } 2153 } 2154 2155 /** 2156 * A cell renderer for Icon values. 2157 */ 2158 private class IconCellRenderer 2159 extends DefaultTableCellRenderer 2160 { IconCellRenderer()2161 IconCellRenderer() 2162 { 2163 setHorizontalAlignment(SwingConstants.CENTER); 2164 } 2165 2166 2167 /** 2168 * Returns the component that is used for rendering the value. 2169 * 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 2176 * 2177 * @return this component (the default table cell renderer) 2178 */ getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)2179 public Component getTableCellRendererComponent(JTable table, Object value, 2180 boolean isSelected, 2181 boolean hasFocus, int row, 2182 int column) 2183 { 2184 super.getTableCellRendererComponent(table, value, isSelected, hasFocus, 2185 row, column); 2186 if (value instanceof Icon) 2187 { 2188 Icon iconValue = (Icon) value; 2189 setIcon(iconValue); 2190 } 2191 else 2192 { 2193 setIcon(null); 2194 } 2195 setText(""); 2196 return this; 2197 } 2198 } 2199 2200 /** 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. 2204 * 2205 * @author Audrius Meskauskas (AudriusA@Bioinformatics.org) 2206 */ 2207 private class TableTextField extends JTextField 2208 { 2209 /** 2210 * Create the text field without the border. 2211 */ TableTextField()2212 TableTextField() 2213 { 2214 setBorder(BorderFactory.createLineBorder(getGridColor(), 2)); 2215 } 2216 } 2217 2218 2219 private static final long serialVersionUID = 3876025080382781659L; 2220 2221 /** 2222 * This table, for referring identically name methods from inner classes. 2223 */ 2224 final JTable this_table = this; 2225 2226 2227 /** 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. 2231 */ 2232 public static final int AUTO_RESIZE_OFF = 0; 2233 2234 /** 2235 * When resizing column <code>i</code>, automatically change only the 2236 * single column <code>i+1</code> to provide or absorb excess space 2237 * requirements. 2238 */ 2239 public static final int AUTO_RESIZE_NEXT_COLUMN = 1; 2240 2241 /** 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. 2245 */ 2246 public static final int AUTO_RESIZE_SUBSEQUENT_COLUMNS = 2; 2247 2248 /** 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. 2253 */ 2254 public static final int AUTO_RESIZE_ALL_COLUMNS = 4; 2255 2256 /** 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. 2260 */ 2261 public static final int AUTO_RESIZE_LAST_COLUMN = 3; 2262 2263 /** 2264 * A table mapping {@link java.lang.Class} objects to 2265 * {@link TableCellEditor} objects. This table is consulted by the 2266 * FIXME 2267 */ 2268 protected Hashtable defaultEditorsByColumnClass = new Hashtable(); 2269 2270 /** 2271 * A table mapping {@link java.lang.Class} objects to 2272 * {@link TableCellEditor} objects. This table is consulted by the 2273 * FIXME 2274 */ 2275 protected Hashtable defaultRenderersByColumnClass = new Hashtable(); 2276 2277 /** 2278 * The column that is edited, -1 if the table is not edited currently. 2279 */ 2280 protected int editingColumn; 2281 2282 /** 2283 * The row that is edited, -1 if the table is not edited currently. 2284 */ 2285 protected int editingRow; 2286 2287 /** 2288 * The component that is used for editing. 2289 * <code>null</code> if the table is not editing currently. 2290 * 2291 */ 2292 protected transient Component editorComp; 2293 2294 2295 /** 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. 2299 * 2300 * @see #setModel(TableModel) 2301 * @see #createDefaultColumnsFromModel() 2302 * @see #setColumnModel(TableColumnModel) 2303 * @see #setAutoCreateColumnsFromModel(boolean) 2304 * @see #getAutoCreateColumnsFromModel() 2305 */ 2306 protected boolean autoCreateColumnsFromModel; 2307 2308 /** 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}. 2313 * 2314 * @see #doLayout() 2315 * @see #setAutoResizeMode(int) 2316 * @see #getAutoResizeMode() 2317 */ 2318 protected int autoResizeMode; 2319 2320 /** 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}. 2325 * 2326 * @see #getRowHeight() 2327 * @see #setRowHeight(int) 2328 * @see TableColumn#getWidth() 2329 * @see TableColumn#setWidth(int) 2330 */ 2331 protected int rowHeight; 2332 2333 /** 2334 * The height in pixels of the gap left between any two rows of the table. 2335 * 2336 * @see #setRowMargin(int) 2337 * @see #getRowHeight() 2338 * @see #getIntercellSpacing() 2339 * @see #setIntercellSpacing(Dimension) 2340 * @see TableColumnModel#getColumnMargin() 2341 * @see TableColumnModel#setColumnMargin(int) 2342 */ 2343 protected int rowMargin; 2344 2345 /** 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. 2351 * 2352 * @see TableColumnModel#getColumnSelectionAllowed() 2353 * @see #setRowSelectionAllowed(boolean) 2354 * @see #getRowSelectionAllowed() 2355 * @see #getCellSelectionEnabled() 2356 * @see #setCellSelectionEnabled(boolean) 2357 */ 2358 protected boolean rowSelectionAllowed; 2359 2360 /** 2361 * Obsolete. Use {@link #rowSelectionAllowed}, {@link 2362 * #getColumnSelectionAllowed}, or the combined methods {@link 2363 * #getCellSelectionEnabled} and {@link #setCellSelectionEnabled(boolean)}. 2364 */ 2365 protected boolean cellSelectionEnabled; 2366 2367 /** 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}. 2372 * 2373 * @see #tableChanged(TableModelEvent) 2374 * @see TableModel#addTableModelListener(TableModelListener) 2375 */ 2376 protected TableModel dataModel; 2377 2378 /** 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> 2385 * 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> 2391 * 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> 2396 * 2397 * @see #setColumnModel(TableColumnModel) 2398 * @see #getColumnModel() 2399 */ 2400 protected TableColumnModel columnModel; 2401 2402 /** 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. 2407 * 2408 * @see #rowSelectionAllowed 2409 * @see #setSelectionModel(ListSelectionModel) 2410 * @see #getSelectionModel() 2411 * @see TableColumnModel#getSelectionModel() 2412 * @see ListSelectionModel#addListSelectionListener(ListSelectionListener) 2413 */ 2414 protected ListSelectionModel selectionModel; 2415 2416 /** 2417 * The current cell editor. 2418 */ 2419 protected TableCellEditor cellEditor; 2420 2421 /** 2422 * Whether or not drag-and-drop is enabled on this table. 2423 * 2424 * @see #setDragEnabled(boolean) 2425 * @see #getDragEnabled() 2426 */ 2427 private boolean dragEnabled; 2428 2429 /** 2430 * The color to paint the grid lines of the table, when either {@link 2431 * #showHorizontalLines} or {@link #showVerticalLines} is set. 2432 * 2433 * @see #setGridColor(Color) 2434 * @see #getGridColor() 2435 */ 2436 protected Color gridColor; 2437 2438 /** 2439 * The size this table would prefer its viewport assume, if it is 2440 * contained in a {@link JScrollPane}. 2441 * 2442 * @see #setPreferredScrollableViewportSize(Dimension) 2443 * @see #getPreferredScrollableViewportSize() 2444 */ 2445 protected Dimension preferredViewportSize; 2446 2447 /** 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. 2451 * 2452 * @see #setSelectionBackground(Color) 2453 * @see #getSelectionBackground() 2454 */ 2455 protected Color selectionBackground; 2456 2457 /** 2458 * The name carried in property change events when the {@link 2459 * #selectionBackground} property changes. 2460 */ 2461 private static final String SELECTION_BACKGROUND_CHANGED_PROPERTY = "selectionBackground"; 2462 2463 /** 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. 2467 * 2468 * @see #setSelectionForeground(Color) 2469 * @see #getSelectionForeground() 2470 */ 2471 protected Color selectionForeground; 2472 2473 /** 2474 * The name carried in property change events when the 2475 * {@link #selectionForeground} property changes. 2476 */ 2477 private static final String SELECTION_FOREGROUND_CHANGED_PROPERTY = "selectionForeground"; 2478 2479 /** 2480 * The showHorizontalLines property. 2481 */ 2482 protected boolean showHorizontalLines; 2483 2484 /** 2485 * The showVerticalLines property. 2486 */ 2487 protected boolean showVerticalLines; 2488 2489 /** 2490 * The tableHeader property. 2491 */ 2492 protected JTableHeader tableHeader; 2493 2494 /** 2495 * The property handler for this table's columns. 2496 */ 2497 TableColumnPropertyChangeHandler tableColumnPropertyChangeHandler = 2498 new TableColumnPropertyChangeHandler(); 2499 2500 /** 2501 * Whether cell editors should receive keyboard focus when the table is 2502 * activated. 2503 */ 2504 private boolean surrendersFocusOnKeystroke = false; 2505 2506 /** 2507 * A Rectangle object to be reused in {@link #getCellRect}. 2508 */ 2509 private Rectangle rectCache = new Rectangle(); 2510 2511 /** 2512 * Indicates if the rowHeight property has been set by a client program or by 2513 * the UI. 2514 * 2515 * @see #setUIProperty(String, Object) 2516 * @see LookAndFeel#installProperty(JComponent, String, Object) 2517 */ 2518 private boolean clientRowHeightSet = false; 2519 2520 /** 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 2526 * object instead. 2527 * 2528 * @see #setRowHeight(int) 2529 * @see #getRowHeight() 2530 * @see #getRowHeight(int) 2531 * @see #setRowHeight(int, int) 2532 */ 2533 private SizeSequence rowHeights; 2534 2535 /** 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. 2538 */ 2539 private transient TableCellEditor booleanInvertingEditor; 2540 2541 /** 2542 * Creates a new <code>JTable</code> instance. 2543 */ JTable()2544 public JTable () 2545 { 2546 this(null, null, null); 2547 } 2548 2549 /** 2550 * Creates a new <code>JTable</code> instance with the given number 2551 * of rows and columns. 2552 * 2553 * @param numRows an <code>int</code> value 2554 * @param numColumns an <code>int</code> value 2555 */ JTable(int numRows, int numColumns)2556 public JTable (int numRows, int numColumns) 2557 { 2558 this(new DefaultTableModel(numRows, numColumns)); 2559 } 2560 2561 /** 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}. 2565 * 2566 * @param data an <code>Object[][]</code> the table data 2567 * @param columnNames an <code>Object[]</code> the column headers 2568 */ JTable(Object[][] data, Object[] columnNames)2569 public JTable(Object[][] data, Object[] columnNames) 2570 { 2571 this(new DefaultTableModel(data, columnNames)); 2572 } 2573 2574 /** 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. 2579 * 2580 * @param model 2581 * the table model. 2582 */ JTable(TableModel model)2583 public JTable (TableModel model) 2584 { 2585 this(model, null, null); 2586 } 2587 2588 /** 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 2594 * related features. 2595 * 2596 * @param dm 2597 * the table data mode 2598 * @param cm 2599 * the table column model 2600 */ JTable(TableModel dm, TableColumnModel cm)2601 public JTable (TableModel dm, TableColumnModel cm) 2602 { 2603 this(dm, cm, null); 2604 } 2605 2606 /** 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. 2610 * 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) 2614 */ JTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm)2615 public JTable (TableModel dm, TableColumnModel cm, ListSelectionModel sm) 2616 { 2617 boolean autoCreate = false; 2618 TableColumnModel columnModel; 2619 if (cm != null) 2620 columnModel = cm; 2621 else 2622 { 2623 columnModel = createDefaultColumnModel(); 2624 autoCreate = true; 2625 } 2626 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). 2630 rowMargin = 1; 2631 columnModel.setColumnMargin(1); 2632 setColumnModel(columnModel); 2633 2634 setSelectionModel(sm == null ? createDefaultSelectionModel() : sm); 2635 setModel(dm == null ? createDefaultDataModel() : dm); 2636 setAutoCreateColumnsFromModel(autoCreate); 2637 initializeLocalVars(); 2638 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 2645 // constructor. 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); 2652 updateUI(); 2653 } 2654 2655 /** 2656 * Creates a new <code>JTable</code> instance that uses data and column 2657 * names, stored in {@link Vector}s. 2658 * 2659 * @param data the table data 2660 * @param columnNames the table column names. 2661 */ JTable(Vector data, Vector columnNames)2662 public JTable(Vector data, Vector columnNames) 2663 { 2664 this(new DefaultTableModel(data, columnNames)); 2665 } 2666 2667 /** 2668 * Initialize local variables to default values. 2669 */ initializeLocalVars()2670 protected void initializeLocalVars() 2671 { 2672 setTableHeader(createDefaultTableHeader()); 2673 if (autoCreateColumnsFromModel) 2674 createDefaultColumnsFromModel(); 2675 this.columnModel.addColumnModelListener(this); 2676 2677 this.autoResizeMode = AUTO_RESIZE_SUBSEQUENT_COLUMNS; 2678 setRowHeight(16); 2679 this.rowMargin = 1; 2680 this.rowSelectionAllowed = true; 2681 2682 // this.accessibleContext = new AccessibleJTable(); 2683 this.cellEditor = null; 2684 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; 2692 } 2693 2694 /** 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. 2698 * 2699 * There is no need the add columns to the table if the default column 2700 * handling is sufficient. 2701 * 2702 * @param column 2703 * the new column to add. 2704 */ addColumn(TableColumn column)2705 public void addColumn(TableColumn column) 2706 { 2707 if (column.getHeaderValue() == null) 2708 { 2709 String name = dataModel.getColumnName(column.getModelIndex()); 2710 column.setHeaderValue(name); 2711 } 2712 2713 columnModel.addColumn(column); 2714 column.addPropertyChangeListener(tableColumnPropertyChangeHandler); 2715 } 2716 2717 /** 2718 * Create the default editors for this table. The default method creates 2719 * the editor for Booleans. 2720 * 2721 * Other fields are edited as strings at the moment. 2722 */ createDefaultEditors()2723 protected void createDefaultEditors() 2724 { 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); 2730 } 2731 2732 /** 2733 * Create the default renderers for this table. The default method creates 2734 * renderers for Boolean, Number, Double, Date, Icon and ImageIcon. 2735 * 2736 */ createDefaultRenderers()2737 protected void createDefaultRenderers() 2738 { 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()); 2746 } 2747 2748 /** 2749 * @deprecated 1.0.2, replaced by <code>new JScrollPane(JTable)</code> 2750 */ createScrollPaneForTable(JTable table)2751 public static JScrollPane createScrollPaneForTable(JTable table) 2752 { 2753 return new JScrollPane(table); 2754 } 2755 2756 /** 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}. 2760 * 2761 * @return the created table column model. 2762 */ createDefaultColumnModel()2763 protected TableColumnModel createDefaultColumnModel() 2764 { 2765 return new DefaultTableColumnModel(); 2766 } 2767 2768 /** 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}. 2772 * 2773 * @return the created table data model. 2774 */ createDefaultDataModel()2775 protected TableModel createDefaultDataModel() 2776 { 2777 return new DefaultTableModel(); 2778 } 2779 2780 /** 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}. 2784 * 2785 * @return the created table data model. 2786 */ createDefaultSelectionModel()2787 protected ListSelectionModel createDefaultSelectionModel() 2788 { 2789 return new DefaultListSelectionModel(); 2790 } 2791 2792 /** 2793 * Create the default table header, if the user - defined table header is not 2794 * provided. 2795 * 2796 * @return the default table header. 2797 */ createDefaultTableHeader()2798 protected JTableHeader createDefaultTableHeader() 2799 { 2800 return new JTableHeader(columnModel); 2801 } 2802 2803 /** 2804 * Invoked when the column is added. Revalidates and repains the table. 2805 */ columnAdded(TableColumnModelEvent event)2806 public void columnAdded (TableColumnModelEvent event) 2807 { 2808 revalidate(); 2809 repaint(); 2810 } 2811 2812 /** 2813 * Invoked when the column margin is changed. 2814 * Revalidates and repains the table. 2815 */ columnMarginChanged(ChangeEvent event)2816 public void columnMarginChanged (ChangeEvent event) 2817 { 2818 revalidate(); 2819 repaint(); 2820 } 2821 2822 /** 2823 * Invoked when the column is moved. Revalidates and repains the table. 2824 */ columnMoved(TableColumnModelEvent event)2825 public void columnMoved (TableColumnModelEvent event) 2826 { 2827 if (isEditing()) 2828 editingCanceled(null); 2829 revalidate(); 2830 repaint(); 2831 } 2832 2833 /** 2834 * Invoked when the column is removed. Revalidates and repains the table. 2835 */ columnRemoved(TableColumnModelEvent event)2836 public void columnRemoved (TableColumnModelEvent event) 2837 { 2838 revalidate(); 2839 repaint(); 2840 } 2841 2842 /** 2843 * Invoked when the the column selection changes, repaints the changed 2844 * columns. It is not recommended to override this method, register the 2845 * listener instead. 2846 */ columnSelectionChanged(ListSelectionEvent event)2847 public void columnSelectionChanged (ListSelectionEvent event) 2848 { 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())); 2856 2857 int minRow = 0; 2858 int maxRow = getRowCount() - 1; 2859 if (getRowSelectionAllowed()) 2860 { 2861 minRow = selectionModel.getMinSelectionIndex(); 2862 maxRow = selectionModel.getMaxSelectionIndex(); 2863 int leadRow = selectionModel.getLeadSelectionIndex(); 2864 if (minRow == -1 && maxRow == -1) 2865 { 2866 minRow = leadRow; 2867 maxRow = leadRow; 2868 } 2869 else 2870 { 2871 // In this case we need to repaint also the range to leadRow, not 2872 // only between min and max. 2873 if (leadRow != -1) 2874 { 2875 minRow = Math.min(minRow, leadRow); 2876 maxRow = Math.max(maxRow, leadRow); 2877 } 2878 } 2879 } 2880 if (minRow != -1 && maxRow != -1) 2881 { 2882 Rectangle first = getCellRect(minRow, idx0, false); 2883 Rectangle last = getCellRect(maxRow, idxn, false); 2884 Rectangle dirty = SwingUtilities.computeUnion(first.x, first.y, 2885 first.width, 2886 first.height, last); 2887 repaint(dirty); 2888 } 2889 } 2890 2891 /** 2892 * Invoked when the editing is cancelled. 2893 */ editingCanceled(ChangeEvent event)2894 public void editingCanceled (ChangeEvent event) 2895 { 2896 if (editorComp!=null) 2897 { 2898 remove(editorComp); 2899 repaint(editorComp.getBounds()); 2900 editorComp = null; 2901 } 2902 } 2903 2904 /** 2905 * Finish the current editing session and update the table with the 2906 * new value by calling {@link #setValueAt}. 2907 * 2908 * @param event the change event 2909 */ editingStopped(ChangeEvent event)2910 public void editingStopped (ChangeEvent event) 2911 { 2912 if (editorComp!=null) 2913 { 2914 remove(editorComp); 2915 setValueAt(cellEditor.getCellEditorValue(), editingRow, editingColumn); 2916 repaint(editorComp.getBounds()); 2917 editorComp = null; 2918 } 2919 requestFocusInWindow(); 2920 } 2921 2922 /** 2923 * Invoked when the table changes. 2924 * <code>null</code> means everything changed. 2925 */ tableChanged(TableModelEvent event)2926 public void tableChanged (TableModelEvent event) 2927 { 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); 2936 else 2937 handleUpdate(event); 2938 } 2939 2940 /** 2941 * Handles a request for complete relayout. This is the case when 2942 * event.getFirstRow() == TableModelEvent.HEADER_ROW. 2943 * 2944 * @param ev the table model event 2945 */ handleCompleteChange(TableModelEvent ev)2946 private void handleCompleteChange(TableModelEvent ev) 2947 { 2948 clearSelection(); 2949 checkSelection(); 2950 rowHeights = null; 2951 if (getAutoCreateColumnsFromModel()) 2952 createDefaultColumnsFromModel(); 2953 else 2954 resizeAndRepaint(); 2955 } 2956 2957 /** 2958 * Handles table model insertions. 2959 * 2960 * @param ev the table model event 2961 */ handleInsert(TableModelEvent ev)2962 private void handleInsert(TableModelEvent ev) 2963 { 2964 // Sync selection model with data model. 2965 int first = ev.getFirstRow(); 2966 if (first < 0) 2967 first = 0; 2968 int last = ev.getLastRow(); 2969 if (last < 0) 2970 last = getRowCount() - 1; 2971 selectionModel.insertIndexInterval(first, last - first + 1, true); 2972 checkSelection(); 2973 2974 // For variable height rows we must update the SizeSequence thing. 2975 if (rowHeights != null) 2976 { 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. 2980 repaint(); 2981 } 2982 else 2983 { 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); 2989 repaint(dirty); 2990 } 2991 revalidate(); 2992 } 2993 2994 /** 2995 * Handles table model deletions. 2996 * 2997 * @param ev the table model event 2998 */ handleDelete(TableModelEvent ev)2999 private void handleDelete(TableModelEvent ev) 3000 { 3001 // Sync selection model with data model. 3002 int first = ev.getFirstRow(); 3003 if (first < 0) 3004 first = 0; 3005 int last = ev.getLastRow(); 3006 if (last < 0) 3007 last = getRowCount() - 1; 3008 3009 selectionModel.removeIndexInterval(first, last); 3010 3011 checkSelection(); 3012 3013 if (dataModel.getRowCount() == 0) 3014 clearSelection(); 3015 3016 // For variable height rows we must update the SizeSequence thing. 3017 if (rowHeights != null) 3018 { 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. 3022 repaint(); 3023 } 3024 else 3025 { 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); 3032 repaint(dirty); 3033 } 3034 revalidate(); 3035 } 3036 3037 /** 3038 * Handles table model updates without structural changes. 3039 * 3040 * @param ev the table model event 3041 */ handleUpdate(TableModelEvent ev)3042 private void handleUpdate(TableModelEvent ev) 3043 { 3044 if (rowHeights == null) 3045 { 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(); 3051 Rectangle dirty; 3052 if (col == TableModelEvent.ALL_COLUMNS) 3053 { 3054 // All columns changed. 3055 dirty = new Rectangle(0, firstRow * getRowHeight(), 3056 getColumnModel().getTotalColumnWidth(), 0); 3057 } 3058 else 3059 { 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); 3064 } 3065 3066 // Now adjust the height of the dirty region. 3067 dirty.height = (lastRow + 1) * getRowHeight(); 3068 // .. and repaint. 3069 repaint(dirty); 3070 } 3071 else 3072 { 3073 // TODO: We repaint the whole thing when the rows have variable 3074 // heights. We might want to handle this better though. 3075 repaint(); 3076 } 3077 } 3078 3079 /** 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 3083 * some rows now. 3084 */ checkSelection()3085 private void checkSelection() 3086 { 3087 TableModel m = getModel(); 3088 ListSelectionModel sm = selectionModel; 3089 if (m != null) 3090 { 3091 int lead = sm.getLeadSelectionIndex(); 3092 int c = m.getRowCount(); 3093 if (c == 0 && lead != -1) 3094 { 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); 3100 } 3101 else if (c != 0 && lead == -1) 3102 { 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 3105 // touched. 3106 if (sm.isSelectedIndex(0)) 3107 sm.addSelectionInterval(0, 0); 3108 else 3109 sm.removeSelectionInterval(0, 0); 3110 } 3111 // Nothing to do in the other cases. 3112 } 3113 } 3114 3115 /** 3116 * Invoked when another table row is selected. It is not recommended 3117 * to override thid method, register the listener instead. 3118 */ valueChanged(ListSelectionEvent event)3119 public void valueChanged (ListSelectionEvent event) 3120 { 3121 // If we are in the editing process, end the editing session. 3122 if (isEditing()) 3123 editingStopped(null); 3124 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, 3132 rect1); 3133 repaint(dirty); 3134 } 3135 3136 /** 3137 * Returns index of the column that contains specified point 3138 * or -1 if this table doesn't contain this point. 3139 * 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. 3143 */ columnAtPoint(Point point)3144 public int columnAtPoint(Point point) 3145 { 3146 int ncols = getColumnCount(); 3147 Dimension gap = getIntercellSpacing(); 3148 TableColumnModel cols = getColumnModel(); 3149 int x = point.x; 3150 3151 for (int i = 0; i < ncols; ++i) 3152 { 3153 int width = cols.getColumn(i).getWidth() 3154 + (gap == null ? 0 : gap.width); 3155 if (0 <= x && x < width) 3156 return i; 3157 x -= width; 3158 } 3159 return -1; 3160 } 3161 3162 /** 3163 * Returns index of the row that contains specified point or -1 if this table 3164 * doesn't contain this point. 3165 * 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. 3169 */ rowAtPoint(Point point)3170 public int rowAtPoint(Point point) 3171 { 3172 if (point != null) 3173 { 3174 int nrows = getRowCount(); 3175 int r; 3176 int y = point.y; 3177 if (rowHeights == null) 3178 { 3179 int height = getRowHeight(); 3180 r = y / height; 3181 } 3182 else 3183 r = rowHeights.getIndex(y); 3184 3185 if (r < 0 || r >= nrows) 3186 return -1; 3187 else 3188 return r; 3189 } 3190 else 3191 return -1; 3192 } 3193 3194 /** 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. 3198 * 3199 * @param row the visible row to get the cell rectangle of 3200 * 3201 * @param column the visible column to get the cell rectangle of, which may 3202 * differ from the {@link #dataModel} column 3203 * 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. 3207 * 3208 * @return a rectangle enclosing the specified cell 3209 */ getCellRect(int row, int column, boolean includeSpacing)3210 public Rectangle getCellRect(int row, 3211 int column, 3212 boolean includeSpacing) 3213 { 3214 Rectangle cellRect = new Rectangle(0, 0, 0, 0); 3215 3216 // Check for valid range vertically. 3217 if (row >= getRowCount()) 3218 { 3219 cellRect.height = getHeight(); 3220 } 3221 else if (row >= 0) 3222 { 3223 cellRect.height = getRowHeight(row); 3224 if (rowHeights == null) 3225 cellRect.y = row * cellRect.height; 3226 else 3227 cellRect.y = rowHeights.getPosition(row); 3228 3229 if (! includeSpacing) 3230 { 3231 // The rounding here is important. 3232 int rMargin = getRowMargin(); 3233 cellRect.y += rMargin / 2; 3234 cellRect.height -= rMargin; 3235 } 3236 } 3237 // else row < 0, y = height = 0 3238 3239 // Check for valid range horizontally. 3240 if (column < 0) 3241 { 3242 if (! getComponentOrientation().isLeftToRight()) 3243 { 3244 cellRect.x = getWidth(); 3245 } 3246 } 3247 else if (column >= getColumnCount()) 3248 { 3249 if (getComponentOrientation().isLeftToRight()) 3250 { 3251 cellRect.x = getWidth(); 3252 } 3253 } 3254 else 3255 { 3256 TableColumnModel tcm = getColumnModel(); 3257 if (getComponentOrientation().isLeftToRight()) 3258 { 3259 for (int i = 0; i < column; i++) 3260 cellRect.x += tcm.getColumn(i).getWidth(); 3261 } 3262 else 3263 { 3264 for (int i = tcm.getColumnCount() - 1; i > column; i--) 3265 cellRect.x += tcm.getColumn(i).getWidth(); 3266 } 3267 cellRect.width = tcm.getColumn(column).getWidth(); 3268 if (! includeSpacing) 3269 { 3270 // The rounding here is important. 3271 int cMargin = tcm.getColumnMargin(); 3272 cellRect.x += cMargin / 2; 3273 cellRect.width -= cMargin; 3274 } 3275 } 3276 3277 return cellRect; 3278 } 3279 clearSelection()3280 public void clearSelection() 3281 { 3282 selectionModel.clearSelection(); 3283 getColumnModel().getSelectionModel().clearSelection(); 3284 } 3285 3286 /** 3287 * Get the value of the selectedRow property by delegation to 3288 * the {@link ListSelectionModel#getMinSelectionIndex} method of the 3289 * {@link #selectionModel} field. 3290 * 3291 * @return The current value of the selectedRow property 3292 */ getSelectedRow()3293 public int getSelectedRow () 3294 { 3295 return selectionModel.getMinSelectionIndex(); 3296 } 3297 3298 /** 3299 * Get the value of the {@link #selectionModel} property. 3300 * 3301 * @return The current value of the property 3302 */ getSelectionModel()3303 public ListSelectionModel getSelectionModel() 3304 { 3305 //Neither Sun nor IBM returns null if rowSelection not allowed 3306 return selectionModel; 3307 } 3308 getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction)3309 public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) 3310 { 3311 int block; 3312 if (orientation == SwingConstants.HORIZONTAL) 3313 { 3314 block = visibleRect.width; 3315 } 3316 else 3317 { 3318 int rowHeight = getRowHeight(); 3319 if (rowHeight > 0) 3320 block = Math.max(rowHeight, // Little hack for useful rounding. 3321 (visibleRect.height / rowHeight) * rowHeight); 3322 else 3323 block = visibleRect.height; 3324 } 3325 return block; 3326 } 3327 3328 /** 3329 * Get the value of the <code>scrollableTracksViewportHeight</code> property. 3330 * 3331 * @return The constant value <code>false</code> 3332 */ getScrollableTracksViewportHeight()3333 public boolean getScrollableTracksViewportHeight() 3334 { 3335 return false; 3336 } 3337 3338 /** 3339 * Get the value of the <code>scrollableTracksViewportWidth</code> property. 3340 * 3341 * @return <code>true</code> unless the {@link #autoResizeMode} property is 3342 * <code>AUTO_RESIZE_OFF</code> 3343 */ getScrollableTracksViewportWidth()3344 public boolean getScrollableTracksViewportWidth() 3345 { 3346 if (autoResizeMode == AUTO_RESIZE_OFF) 3347 return false; 3348 else 3349 return true; 3350 } 3351 3352 /** 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. 3357 * 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 3363 * distance. 3364 * 3365 * @author Roman Kennke (kennke@aicas.com) 3366 */ getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction)3367 public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, 3368 int direction) 3369 { 3370 int unit; 3371 if (orientation == SwingConstants.HORIZONTAL) 3372 unit = 100; 3373 else 3374 { 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) 3379 { 3380 if (direction > 0) 3381 { 3382 // Scroll down. 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; 3387 } 3388 else 3389 { 3390 // Scroll up. 3391 int exposed = visibleRect.y % unit; 3392 if (exposed > 0 && exposed < unit) 3393 unit = exposed; 3394 } 3395 } 3396 } 3397 return unit; 3398 } 3399 3400 /** 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)}. 3406 * 3407 * @param row the cell row 3408 * @param column the cell column 3409 * @return the editor to edit that cell 3410 */ getCellEditor(int row, int column)3411 public TableCellEditor getCellEditor(int row, int column) 3412 { 3413 TableCellEditor editor = columnModel.getColumn(column).getCellEditor(); 3414 3415 if (editor == null) 3416 { 3417 int mcolumn = convertColumnIndexToModel(column); 3418 editor = getDefaultEditor(dataModel.getColumnClass(mcolumn)); 3419 } 3420 3421 return editor; 3422 } 3423 3424 /** 3425 * Get the default editor for editing values of the given type 3426 * (String, Boolean and so on). 3427 * 3428 * @param columnClass the class of the value that will be edited. 3429 * 3430 * @return the editor, suitable for editing this data type 3431 */ getDefaultEditor(Class<?> columnClass)3432 public TableCellEditor getDefaultEditor(Class<?> columnClass) 3433 { 3434 if (defaultEditorsByColumnClass.containsKey(columnClass)) 3435 return (TableCellEditor) defaultEditorsByColumnClass.get(columnClass); 3436 else 3437 { 3438 JTextField t = new TableTextField(); 3439 TableCellEditor r = new DefaultCellEditor(t); 3440 defaultEditorsByColumnClass.put(columnClass, r); 3441 return r; 3442 } 3443 } 3444 3445 /** 3446 * Get the cell renderer for rendering the given cell. 3447 * 3448 * @param row the cell row 3449 * @param column the cell column 3450 * @return the cell renderer to render that cell. 3451 */ getCellRenderer(int row, int column)3452 public TableCellRenderer getCellRenderer(int row, int column) 3453 { 3454 TableCellRenderer renderer = null; 3455 if (columnModel.getColumnCount() > 0) 3456 renderer = columnModel.getColumn(column).getCellRenderer(); 3457 if (renderer == null) 3458 { 3459 int mcolumn = convertColumnIndexToModel(column); 3460 renderer = getDefaultRenderer(dataModel.getColumnClass(mcolumn)); 3461 } 3462 return renderer; 3463 } 3464 3465 /** 3466 * Set default renderer for rendering the given data type. 3467 * 3468 * @param columnClass the data type (String, Boolean and so on) that must be 3469 * rendered. 3470 * @param rend the renderer that will rend this data type 3471 */ setDefaultRenderer(Class<?> columnClass, TableCellRenderer rend)3472 public void setDefaultRenderer(Class<?> columnClass, TableCellRenderer rend) 3473 { 3474 defaultRenderersByColumnClass.put(columnClass, rend); 3475 } 3476 3477 /** 3478 * Get the default renderer for rendering the given data type. 3479 * 3480 * @param columnClass the data that must be rendered 3481 * 3482 * @return the appropriate defauld renderer for rendering that data type. 3483 */ getDefaultRenderer(Class<?> columnClass)3484 public TableCellRenderer getDefaultRenderer(Class<?> columnClass) 3485 { 3486 if (defaultRenderersByColumnClass.containsKey(columnClass)) 3487 return (TableCellRenderer) defaultRenderersByColumnClass.get(columnClass); 3488 else 3489 { 3490 TableCellRenderer r = new DefaultTableCellRenderer(); 3491 defaultRenderersByColumnClass.put(columnClass, r); 3492 return r; 3493 } 3494 } 3495 3496 /** 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 3500 * column headers. 3501 * 3502 * @param vc the column number (0=first). 3503 * 3504 * @return the table column model index of this column. 3505 * 3506 * @see TableColumn#getModelIndex() 3507 */ convertColumnIndexToModel(int vc)3508 public int convertColumnIndexToModel(int vc) 3509 { 3510 if (vc < 0) 3511 return vc; 3512 else 3513 return columnModel.getColumn(vc).getModelIndex(); 3514 } 3515 3516 /** 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 3520 * column headers. 3521 * 3522 * @param mc the table column index (0=first). 3523 * 3524 * @return the table column number in the model 3525 * 3526 * @see TableColumn#getModelIndex() 3527 */ convertColumnIndexToView(int mc)3528 public int convertColumnIndexToView(int mc) 3529 { 3530 if (mc < 0) 3531 return mc; 3532 int ncols = getColumnCount(); 3533 for (int vc = 0; vc < ncols; ++vc) 3534 { 3535 if (columnModel.getColumn(vc).getModelIndex() == mc) 3536 return vc; 3537 } 3538 return -1; 3539 } 3540 3541 /** 3542 * Prepare the renderer for rendering the given cell. 3543 * 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 3547 * 3548 * @return the component which .paint() method will paint the cell. 3549 */ prepareRenderer(TableCellRenderer renderer, int row, int column)3550 public Component prepareRenderer(TableCellRenderer renderer, 3551 int row, 3552 int column) 3553 { 3554 boolean rowSelAllowed = getRowSelectionAllowed(); 3555 boolean colSelAllowed = getColumnSelectionAllowed(); 3556 boolean isSel = false; 3557 if (rowSelAllowed && colSelAllowed || !rowSelAllowed && !colSelAllowed) 3558 isSel = isCellSelected(row, column); 3559 else 3560 isSel = isRowSelected(row) && getRowSelectionAllowed() 3561 || isColumnSelected(column) && getColumnSelectionAllowed(); 3562 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; 3570 3571 return renderer.getTableCellRendererComponent(this, 3572 dataModel.getValueAt(row, 3573 convertColumnIndexToModel(column)), 3574 isSel, 3575 hasFocus, 3576 row, column); 3577 } 3578 3579 3580 /** 3581 * Get the value of the {@link #autoCreateColumnsFromModel} property. 3582 * 3583 * @return The current value of the property 3584 */ getAutoCreateColumnsFromModel()3585 public boolean getAutoCreateColumnsFromModel() 3586 { 3587 return autoCreateColumnsFromModel; 3588 } 3589 3590 /** 3591 * Get the value of the {@link #autoResizeMode} property. 3592 * 3593 * @return The current value of the property 3594 */ getAutoResizeMode()3595 public int getAutoResizeMode() 3596 { 3597 return autoResizeMode; 3598 } 3599 3600 /** 3601 * Get the value of the {@link #rowHeight} property. 3602 * 3603 * @return The current value of the property 3604 */ getRowHeight()3605 public int getRowHeight() 3606 { 3607 return rowHeight; 3608 } 3609 3610 /** 3611 * Get the height of the specified row. 3612 * 3613 * @param row the row whose height to return 3614 */ getRowHeight(int row)3615 public int getRowHeight(int row) 3616 { 3617 int rh = rowHeight; 3618 if (rowHeights != null) 3619 rh = rowHeights.getSize(row); 3620 return rh; 3621 } 3622 3623 3624 /** 3625 * Get the value of the {@link #rowMargin} property. 3626 * 3627 * @return The current value of the property 3628 */ getRowMargin()3629 public int getRowMargin() 3630 { 3631 return rowMargin; 3632 } 3633 3634 /** 3635 * Get the value of the {@link #rowSelectionAllowed} property. 3636 * 3637 * @return The current value of the property 3638 * 3639 * @see #setRowSelectionAllowed(boolean) 3640 */ getRowSelectionAllowed()3641 public boolean getRowSelectionAllowed() 3642 { 3643 return rowSelectionAllowed; 3644 } 3645 3646 /** 3647 * Get the value of the {@link #cellSelectionEnabled} property. 3648 * 3649 * @return The current value of the property 3650 */ getCellSelectionEnabled()3651 public boolean getCellSelectionEnabled() 3652 { 3653 return getColumnSelectionAllowed() && getRowSelectionAllowed(); 3654 } 3655 3656 /** 3657 * Get the value of the {@link #dataModel} property. 3658 * 3659 * @return The current value of the property 3660 */ getModel()3661 public TableModel getModel() 3662 { 3663 return dataModel; 3664 } 3665 3666 /** 3667 * Get the value of the <code>columnCount</code> property by 3668 * delegation to the {@link #columnModel} field. 3669 * 3670 * @return The current value of the columnCount property 3671 */ getColumnCount()3672 public int getColumnCount() 3673 { 3674 return columnModel.getColumnCount(); 3675 } 3676 3677 /** 3678 * Get the value of the <code>rowCount</code> property by 3679 * delegation to the {@link #dataModel} field. 3680 * 3681 * @return The current value of the rowCount property 3682 */ getRowCount()3683 public int getRowCount() 3684 { 3685 return dataModel.getRowCount(); 3686 } 3687 3688 /** 3689 * Get the value of the {@link #columnModel} property. 3690 * 3691 * @return The current value of the property 3692 */ getColumnModel()3693 public TableColumnModel getColumnModel() 3694 { 3695 return columnModel; 3696 } 3697 3698 /** 3699 * Get the value of the <code>selectedColumn</code> property by 3700 * delegation to the {@link #columnModel} field. 3701 * 3702 * @return The current value of the selectedColumn property 3703 */ getSelectedColumn()3704 public int getSelectedColumn() 3705 { 3706 return columnModel.getSelectionModel().getMinSelectionIndex(); 3707 } 3708 countSelections(ListSelectionModel lsm)3709 private static int countSelections(ListSelectionModel lsm) 3710 { 3711 int lo = lsm.getMinSelectionIndex(); 3712 int hi = lsm.getMaxSelectionIndex(); 3713 int sum = 0; 3714 if (lo != -1 && hi != -1) 3715 { 3716 switch (lsm.getSelectionMode()) 3717 { 3718 case ListSelectionModel.SINGLE_SELECTION: 3719 sum = 1; 3720 break; 3721 3722 case ListSelectionModel.SINGLE_INTERVAL_SELECTION: 3723 sum = hi - lo + 1; 3724 break; 3725 3726 case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION: 3727 for (int i = lo; i <= hi; ++i) 3728 if (lsm.isSelectedIndex(i)) 3729 ++sum; 3730 break; 3731 } 3732 } 3733 return sum; 3734 } 3735 getSelections(ListSelectionModel lsm)3736 private static int[] getSelections(ListSelectionModel lsm) 3737 { 3738 int sz = countSelections(lsm); 3739 int [] ret = new int[sz]; 3740 3741 int lo = lsm.getMinSelectionIndex(); 3742 int hi = lsm.getMaxSelectionIndex(); 3743 int j = 0; 3744 if (lo != -1 && hi != -1) 3745 { 3746 switch (lsm.getSelectionMode()) 3747 { 3748 case ListSelectionModel.SINGLE_SELECTION: 3749 ret[0] = lo; 3750 break; 3751 3752 case ListSelectionModel.SINGLE_INTERVAL_SELECTION: 3753 for (int i = lo; i <= hi; ++i) 3754 ret[j++] = i; 3755 break; 3756 3757 case ListSelectionModel.MULTIPLE_INTERVAL_SELECTION: 3758 for (int i = lo; i <= hi; ++i) 3759 if (lsm.isSelectedIndex(i)) 3760 ret[j++] = i; 3761 break; 3762 } 3763 } 3764 return ret; 3765 } 3766 3767 /** 3768 * Get the value of the <code>selectedColumnCount</code> property by 3769 * delegation to the {@link #columnModel} field. 3770 * 3771 * @return The current value of the selectedColumnCount property 3772 */ getSelectedColumnCount()3773 public int getSelectedColumnCount() 3774 { 3775 return countSelections(columnModel.getSelectionModel()); 3776 } 3777 3778 /** 3779 * Get the value of the <code>selectedColumns</code> property by 3780 * delegation to the {@link #columnModel} field. 3781 * 3782 * @return The current value of the selectedColumns property 3783 */ getSelectedColumns()3784 public int[] getSelectedColumns() 3785 { 3786 return getSelections(columnModel.getSelectionModel()); 3787 } 3788 3789 /** 3790 * Get the value of the <code>columnSelectionAllowed</code> property. 3791 * 3792 * @return The current value of the columnSelectionAllowed property 3793 * 3794 * @see #setColumnSelectionAllowed(boolean) 3795 */ getColumnSelectionAllowed()3796 public boolean getColumnSelectionAllowed() 3797 { 3798 return getColumnModel().getColumnSelectionAllowed(); 3799 } 3800 3801 /** 3802 * Get the value of the <code>selectedRowCount</code> property by 3803 * delegation to the {@link #selectionModel} field. 3804 * 3805 * @return The current value of the selectedRowCount property 3806 */ getSelectedRowCount()3807 public int getSelectedRowCount() 3808 { 3809 return countSelections(selectionModel); 3810 } 3811 3812 /** 3813 * Get the value of the <code>selectedRows</code> property by 3814 * delegation to the {@link #selectionModel} field. 3815 * 3816 * @return The current value of the selectedRows property 3817 */ getSelectedRows()3818 public int[] getSelectedRows() 3819 { 3820 return getSelections(selectionModel); 3821 } 3822 3823 /** 3824 * Get the value of the {@link #accessibleContext} property. 3825 * 3826 * @return The current value of the property 3827 */ getAccessibleContext()3828 public AccessibleContext getAccessibleContext() 3829 { 3830 if (accessibleContext == null) 3831 { 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); 3838 3839 accessibleContext = ctx; 3840 } 3841 return accessibleContext; 3842 } 3843 3844 /** 3845 * Get the value of the {@link #cellEditor} property. 3846 * 3847 * @return The current value of the property 3848 */ getCellEditor()3849 public TableCellEditor getCellEditor() 3850 { 3851 return cellEditor; 3852 } 3853 3854 /** 3855 * Get the value of the {@link #dragEnabled} property. 3856 * 3857 * @return The current value of the property 3858 */ getDragEnabled()3859 public boolean getDragEnabled() 3860 { 3861 return dragEnabled; 3862 } 3863 3864 /** 3865 * Get the value of the {@link #gridColor} property. 3866 * 3867 * @return The current value of the property 3868 */ getGridColor()3869 public Color getGridColor() 3870 { 3871 return gridColor; 3872 } 3873 3874 /** 3875 * Get the value of the <code>intercellSpacing</code> property. 3876 * 3877 * @return The current value of the property 3878 */ getIntercellSpacing()3879 public Dimension getIntercellSpacing() 3880 { 3881 return new Dimension(columnModel.getColumnMargin(), rowMargin); 3882 } 3883 3884 /** 3885 * Get the value of the {@link #preferredViewportSize} property. 3886 * 3887 * @return The current value of the property 3888 */ getPreferredScrollableViewportSize()3889 public Dimension getPreferredScrollableViewportSize() 3890 { 3891 return preferredViewportSize; 3892 } 3893 3894 /** 3895 * Get the value of the {@link #selectionBackground} property. 3896 * 3897 * @return The current value of the property 3898 */ getSelectionBackground()3899 public Color getSelectionBackground() 3900 { 3901 return selectionBackground; 3902 } 3903 3904 /** 3905 * Get the value of the {@link #selectionForeground} property. 3906 * 3907 * @return The current value of the property 3908 */ getSelectionForeground()3909 public Color getSelectionForeground() 3910 { 3911 return selectionForeground; 3912 } 3913 3914 /** 3915 * Get the value of the {@link #showHorizontalLines} property. 3916 * 3917 * @return The current value of the property 3918 */ getShowHorizontalLines()3919 public boolean getShowHorizontalLines() 3920 { 3921 return showHorizontalLines; 3922 } 3923 3924 /** 3925 * Get the value of the {@link #showVerticalLines} property. 3926 * 3927 * @return The current value of the property 3928 */ getShowVerticalLines()3929 public boolean getShowVerticalLines() 3930 { 3931 return showVerticalLines; 3932 } 3933 3934 /** 3935 * Get the value of the {@link #tableHeader} property. 3936 * 3937 * @return The current value of the property 3938 */ getTableHeader()3939 public JTableHeader getTableHeader() 3940 { 3941 return tableHeader; 3942 } 3943 3944 /** 3945 * Removes specified column from displayable columns of this table. 3946 * 3947 * @param column column to removed 3948 */ removeColumn(TableColumn column)3949 public void removeColumn(TableColumn column) 3950 { 3951 columnModel.removeColumn(column); 3952 } 3953 3954 /** 3955 * Moves column at the specified index to new given location. 3956 * 3957 * @param column index of the column to move 3958 * @param targetColumn index specifying new location of the column 3959 */ moveColumn(int column,int targetColumn)3960 public void moveColumn(int column,int targetColumn) 3961 { 3962 columnModel.moveColumn(column, targetColumn); 3963 } 3964 3965 /** 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. 3969 * 3970 * @param autoCreate the new value of the flag. 3971 */ setAutoCreateColumnsFromModel(boolean autoCreate)3972 public void setAutoCreateColumnsFromModel(boolean autoCreate) 3973 { 3974 if (autoCreateColumnsFromModel != autoCreate) 3975 { 3976 autoCreateColumnsFromModel = autoCreate; 3977 if (autoCreate) 3978 createDefaultColumnsFromModel(); 3979 } 3980 } 3981 3982 /** 3983 * Set the value of the {@link #autoResizeMode} property. 3984 * 3985 * @param a The new value of the autoResizeMode property 3986 */ setAutoResizeMode(int a)3987 public void setAutoResizeMode(int a) 3988 { 3989 autoResizeMode = a; 3990 revalidate(); 3991 repaint(); 3992 } 3993 3994 /** 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)}. 3997 * 3998 * @param r the height to set for all rows 3999 * 4000 * @see #getRowHeight() 4001 * @see #setRowHeight(int, int) 4002 * @see #getRowHeight(int) 4003 */ setRowHeight(int r)4004 public void setRowHeight(int r) 4005 { 4006 if (r < 1) 4007 throw new IllegalArgumentException(); 4008 4009 clientRowHeightSet = true; 4010 4011 rowHeight = r; 4012 rowHeights = null; 4013 revalidate(); 4014 repaint(); 4015 } 4016 4017 /** 4018 * Sets the height of a single row in the table. 4019 * 4020 * @param rh the new row height 4021 * @param row the row to change the height of 4022 */ setRowHeight(int row, int rh)4023 public void setRowHeight(int row, int rh) 4024 { 4025 if (rowHeights == null) 4026 { 4027 rowHeights = new SizeSequence(getRowCount(), rowHeight); 4028 } 4029 rowHeights.setSize(row, rh); 4030 } 4031 4032 /** 4033 * Set the value of the {@link #rowMargin} property. 4034 * 4035 * @param r The new value of the rowMargin property 4036 */ setRowMargin(int r)4037 public void setRowMargin(int r) 4038 { 4039 rowMargin = r; 4040 revalidate(); 4041 repaint(); 4042 } 4043 4044 /** 4045 * Set the value of the {@link #rowSelectionAllowed} property. 4046 * 4047 * @param r The new value of the rowSelectionAllowed property 4048 * 4049 * @see #getRowSelectionAllowed() 4050 */ setRowSelectionAllowed(boolean r)4051 public void setRowSelectionAllowed(boolean r) 4052 { 4053 if (rowSelectionAllowed != r) 4054 { 4055 rowSelectionAllowed = r; 4056 firePropertyChange("rowSelectionAllowed", !r, r); 4057 repaint(); 4058 } 4059 } 4060 4061 /** 4062 * Set the value of the {@link #cellSelectionEnabled} property. 4063 * 4064 * @param c The new value of the cellSelectionEnabled property 4065 */ setCellSelectionEnabled(boolean c)4066 public void setCellSelectionEnabled(boolean c) 4067 { 4068 setColumnSelectionAllowed(c); 4069 setRowSelectionAllowed(c); 4070 // for backward-compatibility sake: 4071 cellSelectionEnabled = true; 4072 } 4073 4074 /** 4075 * <p>Set the value of the {@link #dataModel} property.</p> 4076 * 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> 4080 * 4081 * @param m The new value of the model property 4082 */ setModel(TableModel m)4083 public void setModel(TableModel m) 4084 { 4085 // Throw exception is m is null. 4086 if (m == null) 4087 throw new IllegalArgumentException(); 4088 4089 // Don't do anything if setting the current model again. 4090 if (dataModel == m) 4091 return; 4092 4093 TableModel oldModel = dataModel; 4094 4095 // Remove table as TableModelListener from old model. 4096 if (dataModel != null) 4097 dataModel.removeTableModelListener(this); 4098 4099 if (m != null) 4100 { 4101 // Set property. 4102 dataModel = m; 4103 4104 // Add table as TableModelListener to new model. 4105 dataModel.addTableModelListener(this); 4106 4107 // Notify the tableChanged method. 4108 tableChanged(new TableModelEvent(dataModel, 4109 TableModelEvent.HEADER_ROW)); 4110 4111 // Automatically create columns. 4112 if (autoCreateColumnsFromModel) 4113 createDefaultColumnsFromModel(); 4114 } 4115 4116 // This property is bound, so we fire a property change event. 4117 firePropertyChange("model", oldModel, dataModel); 4118 4119 // Repaint table. 4120 revalidate(); 4121 repaint(); 4122 } 4123 4124 /** 4125 * <p>Set the value of the {@link #columnModel} property.</p> 4126 * 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> 4130 * 4131 * @param c The new value of the columnModel property 4132 */ setColumnModel(TableColumnModel c)4133 public void setColumnModel(TableColumnModel c) 4134 { 4135 if (c == null) 4136 throw new IllegalArgumentException(); 4137 TableColumnModel tmp = columnModel; 4138 if (tmp != null) 4139 tmp.removeColumnModelListener(this); 4140 if (c != null) 4141 c.addColumnModelListener(this); 4142 columnModel = c; 4143 if (dataModel != null && columnModel != null) 4144 { 4145 int ncols = getColumnCount(); 4146 TableColumn column; 4147 for (int i = 0; i < ncols; ++i) 4148 { 4149 column = columnModel.getColumn(i); 4150 if (column.getHeaderValue()==null) 4151 column.setHeaderValue(dataModel.getColumnName(i)); 4152 } 4153 } 4154 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); 4159 4160 revalidate(); 4161 repaint(); 4162 } 4163 4164 /** 4165 * Set the value of the <code>columnSelectionAllowed</code> property. 4166 * 4167 * @param c The new value of the property 4168 * 4169 * @see #getColumnSelectionAllowed() 4170 */ setColumnSelectionAllowed(boolean c)4171 public void setColumnSelectionAllowed(boolean c) 4172 { 4173 if (columnModel.getColumnSelectionAllowed() != c) 4174 { 4175 columnModel.setColumnSelectionAllowed(c); 4176 firePropertyChange("columnSelectionAllowed", !c, c); 4177 repaint(); 4178 } 4179 } 4180 4181 /** 4182 * <p>Set the value of the {@link #selectionModel} property.</p> 4183 * 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> 4187 * 4188 * @param s The new value of the selectionModel property 4189 */ setSelectionModel(ListSelectionModel s)4190 public void setSelectionModel(ListSelectionModel s) 4191 { 4192 if (s == null) 4193 throw new IllegalArgumentException(); 4194 ListSelectionModel tmp = selectionModel; 4195 if (tmp != null) 4196 tmp.removeListSelectionListener(this); 4197 if (s != null) 4198 s.addListSelectionListener(this); 4199 selectionModel = s; 4200 checkSelection(); 4201 } 4202 4203 /** 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. 4207 * 4208 * @param s The new value of the property 4209 */ setSelectionMode(int s)4210 public void setSelectionMode(int s) 4211 { 4212 selectionModel.setSelectionMode(s); 4213 columnModel.getSelectionModel().setSelectionMode(s); 4214 4215 repaint(); 4216 } 4217 4218 /** 4219 * <p>Set the value of the {@link #cellEditor} property.</p> 4220 * 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> 4224 * 4225 * @param c The new value of the cellEditor property 4226 */ setCellEditor(TableCellEditor c)4227 public void setCellEditor(TableCellEditor c) 4228 { 4229 TableCellEditor tmp = cellEditor; 4230 if (tmp != null) 4231 tmp.removeCellEditorListener(this); 4232 if (c != null) 4233 c.addCellEditorListener(this); 4234 cellEditor = c; 4235 } 4236 4237 /** 4238 * Set the value of the {@link #dragEnabled} property. 4239 * 4240 * @param d The new value of the dragEnabled property 4241 */ setDragEnabled(boolean d)4242 public void setDragEnabled(boolean d) 4243 { 4244 dragEnabled = d; 4245 } 4246 4247 /** 4248 * Set the value of the {@link #gridColor} property. 4249 * 4250 * @param g The new value of the gridColor property 4251 */ setGridColor(Color g)4252 public void setGridColor(Color g) 4253 { 4254 gridColor = g; 4255 repaint(); 4256 } 4257 4258 /** 4259 * Set the value of the <code>intercellSpacing</code> property. 4260 * 4261 * @param i The new value of the intercellSpacing property 4262 */ setIntercellSpacing(Dimension i)4263 public void setIntercellSpacing(Dimension i) 4264 { 4265 rowMargin = i.height; 4266 columnModel.setColumnMargin(i.width); 4267 repaint(); 4268 } 4269 4270 /** 4271 * Set the value of the {@link #preferredViewportSize} property. 4272 * 4273 * @param p The new value of the preferredViewportSize property 4274 */ setPreferredScrollableViewportSize(Dimension p)4275 public void setPreferredScrollableViewportSize(Dimension p) 4276 { 4277 preferredViewportSize = p; 4278 revalidate(); 4279 repaint(); 4280 } 4281 4282 /** 4283 * <p>Set the value of the {@link #selectionBackground} property.</p> 4284 * 4285 * <p>Fire a PropertyChangeEvent with name {@link 4286 * #SELECTION_BACKGROUND_CHANGED_PROPERTY} to registered listeners, if 4287 * selectionBackground changed.</p> 4288 * 4289 * @param s The new value of the selectionBackground property 4290 */ setSelectionBackground(Color s)4291 public void setSelectionBackground(Color s) 4292 { 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); 4299 repaint(); 4300 } 4301 4302 /** 4303 * <p>Set the value of the {@link #selectionForeground} property.</p> 4304 * 4305 * <p>Fire a PropertyChangeEvent with name {@link 4306 * #SELECTION_FOREGROUND_CHANGED_PROPERTY} to registered listeners, if 4307 * selectionForeground changed.</p> 4308 * 4309 * @param s The new value of the selectionForeground property 4310 */ setSelectionForeground(Color s)4311 public void setSelectionForeground(Color s) 4312 { 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); 4319 repaint(); 4320 } 4321 4322 /** 4323 * Set the value of the <code>showGrid</code> property. 4324 * 4325 * @param s The new value of the showGrid property 4326 */ setShowGrid(boolean s)4327 public void setShowGrid(boolean s) 4328 { 4329 setShowVerticalLines(s); 4330 setShowHorizontalLines(s); 4331 } 4332 4333 /** 4334 * Set the value of the {@link #showHorizontalLines} property. 4335 * 4336 * @param s The new value of the showHorizontalLines property 4337 */ setShowHorizontalLines(boolean s)4338 public void setShowHorizontalLines(boolean s) 4339 { 4340 showHorizontalLines = s; 4341 repaint(); 4342 } 4343 4344 /** 4345 * Set the value of the {@link #showVerticalLines} property. 4346 * 4347 * @param s The new value of the showVerticalLines property 4348 */ setShowVerticalLines(boolean s)4349 public void setShowVerticalLines(boolean s) 4350 { 4351 showVerticalLines = s; 4352 repaint(); 4353 } 4354 4355 /** 4356 * Set the value of the {@link #tableHeader} property. 4357 * 4358 * @param t The new value of the tableHeader property 4359 */ setTableHeader(JTableHeader t)4360 public void setTableHeader(JTableHeader t) 4361 { 4362 if (tableHeader != null) 4363 tableHeader.setTable(null); 4364 tableHeader = t; 4365 if (tableHeader != null) 4366 tableHeader.setTable(this); 4367 revalidate(); 4368 repaint(); 4369 } 4370 configureEnclosingScrollPane()4371 protected void configureEnclosingScrollPane() 4372 { 4373 JScrollPane jsp = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, this); 4374 if (jsp != null && tableHeader != null) 4375 { 4376 jsp.setColumnHeaderView(tableHeader); 4377 } 4378 } 4379 unconfigureEnclosingScrollPane()4380 protected void unconfigureEnclosingScrollPane() 4381 { 4382 JScrollPane jsp = (JScrollPane) SwingUtilities.getAncestorOfClass(JScrollPane.class, this); 4383 if (jsp != null) 4384 { 4385 jsp.setColumnHeaderView(null); 4386 } 4387 } 4388 4389 addNotify()4390 public void addNotify() 4391 { 4392 super.addNotify(); 4393 configureEnclosingScrollPane(); 4394 } 4395 removeNotify()4396 public void removeNotify() 4397 { 4398 super.addNotify(); 4399 unconfigureEnclosingScrollPane(); 4400 } 4401 4402 4403 /** 4404 * This distributes the superfluous width in a table evenly on its columns. 4405 * 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. 4408 * 4409 * TODO: correctly implement the algorithm described in the JavaDoc 4410 */ distributeSpill(TableColumn[] cols, int spill)4411 private void distributeSpill(TableColumn[] cols, int spill) 4412 { 4413 int average = spill / cols.length; 4414 for (int i = 0; i < cols.length; i++) 4415 { 4416 if (cols[i] != null) 4417 cols[i].setWidth(cols[i].getPreferredWidth() + average); 4418 } 4419 } 4420 4421 /** 4422 * This distributes the superfluous width in a table, setting the width of the 4423 * column being resized strictly to its preferred width. 4424 */ distributeSpillResizing(TableColumn[] cols, int spill, TableColumn resizeIt)4425 private void distributeSpillResizing(TableColumn[] cols, int spill, 4426 TableColumn resizeIt) 4427 { 4428 int average = 0; 4429 if (cols.length != 1) 4430 average = spill / (cols.length-1); 4431 for (int i = 0; i < cols.length; i++) 4432 { 4433 if (cols[i] != null && !cols[i].equals(resizeIt)) 4434 cols[i].setWidth(cols[i].getPreferredWidth() + average); 4435 } 4436 resizeIt.setWidth(resizeIt.getPreferredWidth()); 4437 } 4438 4439 /** 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. 4444 * 4445 * @see TableColumn#setPreferredWidth(int) 4446 */ doLayout()4447 public void doLayout() 4448 { 4449 TableColumn resizingColumn = null; 4450 4451 int ncols = columnModel.getColumnCount(); 4452 if (ncols < 1) 4453 return; 4454 4455 int prefSum = 0; 4456 int rCol = -1; 4457 4458 if (tableHeader != null) 4459 resizingColumn = tableHeader.getResizingColumn(); 4460 4461 for (int i = 0; i < ncols; ++i) 4462 { 4463 TableColumn col = columnModel.getColumn(i); 4464 int p = col.getPreferredWidth(); 4465 prefSum += p; 4466 if (resizingColumn == col) 4467 rCol = i; 4468 } 4469 4470 int spill = getWidth() - prefSum; 4471 4472 if (resizingColumn != null) 4473 { 4474 TableColumn col; 4475 TableColumn [] cols; 4476 4477 switch (getAutoResizeMode()) 4478 { 4479 case AUTO_RESIZE_LAST_COLUMN: 4480 col = columnModel.getColumn(ncols-1); 4481 col.setWidth(col.getPreferredWidth() + spill); 4482 break; 4483 4484 case AUTO_RESIZE_NEXT_COLUMN: 4485 col = columnModel.getColumn(ncols-1); 4486 col.setWidth(col.getPreferredWidth() + spill); 4487 break; 4488 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); 4494 break; 4495 4496 case AUTO_RESIZE_SUBSEQUENT_COLUMNS: 4497 4498 // Subtract the width of the non-resized columns from the spill. 4499 int w = 0; 4500 int wp = 0; 4501 TableColumn column; 4502 for (int i = 0; i < rCol; i++) 4503 { 4504 column = columnModel.getColumn(i); 4505 w += column.getWidth(); 4506 wp+= column.getPreferredWidth(); 4507 } 4508 4509 // The number of columns right from the column being resized. 4510 int n = ncols-rCol-1; 4511 if (n>0) 4512 { 4513 // If there are any columns on the right sied to resize. 4514 spill = (getWidth()-w) - (prefSum-wp); 4515 int average = spill / n; 4516 4517 // For all columns right from the column being resized: 4518 for (int i = rCol+1; i < ncols; i++) 4519 { 4520 column = columnModel.getColumn(i); 4521 column.setWidth(column.getPreferredWidth() + average); 4522 } 4523 } 4524 resizingColumn.setWidth(resizingColumn.getPreferredWidth()); 4525 break; 4526 4527 case AUTO_RESIZE_OFF: 4528 default: 4529 int prefWidth = resizingColumn.getPreferredWidth(); 4530 resizingColumn.setWidth(prefWidth); 4531 } 4532 } 4533 else 4534 { 4535 TableColumn[] cols = new TableColumn[ncols]; 4536 4537 for (int i = 0; i < ncols; ++i) 4538 cols[i] = columnModel.getColumn(i); 4539 4540 distributeSpill(cols, spill); 4541 } 4542 4543 if (editorComp!=null) 4544 moveToCellBeingEdited(editorComp); 4545 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()); 4551 } 4552 4553 /** 4554 * Get the left boundary of the rectangle which changes during the column 4555 * resizing. 4556 */ getLeftResizingBoundary()4557 int getLeftResizingBoundary() 4558 { 4559 if (tableHeader == null || getAutoResizeMode() == AUTO_RESIZE_ALL_COLUMNS) 4560 return 0; 4561 else 4562 { 4563 TableColumn resizingColumn = tableHeader.getResizingColumn(); 4564 if (resizingColumn == null) 4565 return 0; 4566 4567 int rc = convertColumnIndexToView(resizingColumn.getModelIndex()); 4568 int p = 0; 4569 4570 for (int i = 0; i < rc; i++) 4571 p += columnModel.getColumn(i).getWidth(); 4572 4573 return p; 4574 } 4575 } 4576 4577 4578 /** 4579 * @deprecated Replaced by <code>doLayout()</code> 4580 */ sizeColumnsToFit(boolean lastColumnOnly)4581 public void sizeColumnsToFit(boolean lastColumnOnly) 4582 { 4583 doLayout(); 4584 } 4585 4586 /** 4587 * Obsolete since JDK 1.4. Please use <code>doLayout()</code>. 4588 */ sizeColumnsToFit(int resizingColumn)4589 public void sizeColumnsToFit(int resizingColumn) 4590 { 4591 doLayout(); 4592 } 4593 getUIClassID()4594 public String getUIClassID() 4595 { 4596 return "TableUI"; 4597 } 4598 4599 /** 4600 * This method returns the table's UI delegate. 4601 * 4602 * @return The table's UI delegate. 4603 */ getUI()4604 public TableUI getUI() 4605 { 4606 return (TableUI) ui; 4607 } 4608 4609 /** 4610 * This method sets the table's UI delegate. 4611 * 4612 * @param ui The table's UI delegate. 4613 */ setUI(TableUI ui)4614 public void setUI(TableUI ui) 4615 { 4616 super.setUI(ui); 4617 // The editors and renderers must be recreated because they constructors 4618 // may use the look and feel properties. 4619 createDefaultEditors(); 4620 createDefaultRenderers(); 4621 } 4622 updateUI()4623 public void updateUI() 4624 { 4625 setUI((TableUI) UIManager.getUI(this)); 4626 } 4627 4628 /** 4629 * Get the class (datatype) of the column. The cells are rendered and edited 4630 * differently, depending from they data type. 4631 * 4632 * @param column the column (not the model index). 4633 * 4634 * @return the class, defining data type of that column (String.class for 4635 * String, Boolean.class for boolean and so on). 4636 */ getColumnClass(int column)4637 public Class<?> getColumnClass(int column) 4638 { 4639 return getModel().getColumnClass(convertColumnIndexToModel(column)); 4640 } 4641 4642 /** 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 4646 * is calculated by 4647 * {@link javax.swing.table.AbstractTableModel#getColumnName(int)}. 4648 * 4649 * @param column the column 4650 * 4651 * @return the name of that column. 4652 */ getColumnName(int column)4653 public String getColumnName(int column) 4654 { 4655 int modelColumn = columnModel.getColumn(column).getModelIndex(); 4656 return dataModel.getColumnName(modelColumn); 4657 } 4658 4659 /** 4660 * Get the column, currently being edited 4661 * 4662 * @return the column, currently being edited. 4663 */ getEditingColumn()4664 public int getEditingColumn() 4665 { 4666 return editingColumn; 4667 } 4668 4669 /** 4670 * Set the column, currently being edited 4671 * 4672 * @param column the column, currently being edited. 4673 */ setEditingColumn(int column)4674 public void setEditingColumn(int column) 4675 { 4676 editingColumn = column; 4677 } 4678 4679 /** 4680 * Get the row currently being edited. 4681 * 4682 * @return the row, currently being edited. 4683 */ getEditingRow()4684 public int getEditingRow() 4685 { 4686 return editingRow; 4687 } 4688 4689 /** 4690 * Set the row currently being edited. 4691 * 4692 * @param row the row, that will be edited 4693 */ setEditingRow(int row)4694 public void setEditingRow(int row) 4695 { 4696 editingRow = row; 4697 } 4698 4699 /** 4700 * Get the editor component that is currently editing one of the cells 4701 * 4702 * @return the editor component or null, if none of the cells is being 4703 * edited. 4704 */ getEditorComponent()4705 public Component getEditorComponent() 4706 { 4707 return editorComp; 4708 } 4709 4710 /** 4711 * Check if one of the table cells is currently being edited. 4712 * 4713 * @return true if there is a cell being edited. 4714 */ isEditing()4715 public boolean isEditing() 4716 { 4717 return editorComp != null; 4718 } 4719 4720 /** 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 4723 * the check box. 4724 * 4725 * @param columnClass the column data type 4726 * @param editor the editor that will edit this data type 4727 * 4728 * @see TableModel#getColumnClass(int) 4729 */ setDefaultEditor(Class<?> columnClass, TableCellEditor editor)4730 public void setDefaultEditor(Class<?> columnClass, TableCellEditor editor) 4731 { 4732 if (editor != null) 4733 defaultEditorsByColumnClass.put(columnClass, editor); 4734 else 4735 defaultEditorsByColumnClass.remove(columnClass); 4736 } 4737 addColumnSelectionInterval(int index0, int index1)4738 public void addColumnSelectionInterval(int index0, int index1) 4739 { 4740 if ((index0 < 0 || index0 > (getColumnCount()-1) 4741 || index1 < 0 || index1 > (getColumnCount()-1))) 4742 throw new IllegalArgumentException("Column index out of range."); 4743 4744 getColumnModel().getSelectionModel().addSelectionInterval(index0, index1); 4745 } 4746 addRowSelectionInterval(int index0, int index1)4747 public void addRowSelectionInterval(int index0, int index1) 4748 { 4749 if ((index0 < 0 || index0 > (getRowCount()-1) 4750 || index1 < 0 || index1 > (getRowCount()-1))) 4751 throw new IllegalArgumentException("Row index out of range."); 4752 4753 getSelectionModel().addSelectionInterval(index0, index1); 4754 } 4755 setColumnSelectionInterval(int index0, int index1)4756 public void setColumnSelectionInterval(int index0, int index1) 4757 { 4758 if ((index0 < 0 || index0 > (getColumnCount()-1) 4759 || index1 < 0 || index1 > (getColumnCount()-1))) 4760 throw new IllegalArgumentException("Column index out of range."); 4761 4762 getColumnModel().getSelectionModel().setSelectionInterval(index0, index1); 4763 } 4764 setRowSelectionInterval(int index0, int index1)4765 public void setRowSelectionInterval(int index0, int index1) 4766 { 4767 if ((index0 < 0 || index0 > (getRowCount()-1) 4768 || index1 < 0 || index1 > (getRowCount()-1))) 4769 throw new IllegalArgumentException("Row index out of range."); 4770 4771 getSelectionModel().setSelectionInterval(index0, index1); 4772 } 4773 removeColumnSelectionInterval(int index0, int index1)4774 public void removeColumnSelectionInterval(int index0, int index1) 4775 { 4776 if ((index0 < 0 || index0 > (getColumnCount()-1) 4777 || index1 < 0 || index1 > (getColumnCount()-1))) 4778 throw new IllegalArgumentException("Column index out of range."); 4779 4780 getColumnModel().getSelectionModel().removeSelectionInterval(index0, index1); 4781 } 4782 removeRowSelectionInterval(int index0, int index1)4783 public void removeRowSelectionInterval(int index0, int index1) 4784 { 4785 if ((index0 < 0 || index0 > (getRowCount()-1) 4786 || index1 < 0 || index1 > (getRowCount()-1))) 4787 throw new IllegalArgumentException("Row index out of range."); 4788 4789 getSelectionModel().removeSelectionInterval(index0, index1); 4790 } 4791 4792 /** 4793 * Checks if the given column is selected. 4794 * 4795 * @param column the column 4796 * 4797 * @return true if the column is selected (as reported by the selection 4798 * model, associated with the column model), false otherwise. 4799 */ isColumnSelected(int column)4800 public boolean isColumnSelected(int column) 4801 { 4802 return getColumnModel().getSelectionModel().isSelectedIndex(column); 4803 } 4804 4805 /** 4806 * Checks if the given row is selected. 4807 * 4808 * @param row the row 4809 * 4810 * @return true if the row is selected (as reported by the selection model), 4811 * false otherwise. 4812 */ isRowSelected(int row)4813 public boolean isRowSelected(int row) 4814 { 4815 return getSelectionModel().isSelectedIndex(row); 4816 } 4817 4818 /** 4819 * Checks if the given cell is selected. The cell is selected if both 4820 * the cell row and the cell column are selected. 4821 * 4822 * @param row the cell row 4823 * @param column the cell column 4824 * 4825 * @return true if the cell is selected, false otherwise 4826 */ isCellSelected(int row, int column)4827 public boolean isCellSelected(int row, int column) 4828 { 4829 return isRowSelected(row) && isColumnSelected(column); 4830 } 4831 4832 /** 4833 * Select all table. 4834 */ selectAll()4835 public void selectAll() 4836 { 4837 // The table is empty - nothing to do! 4838 if (getRowCount() == 0 || getColumnCount() == 0) 4839 return; 4840 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 4845 // indices 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); 4852 } 4853 4854 /** 4855 * Get the cell value at the given position. 4856 * 4857 * @param row the row to get the value 4858 * @param column the actual column number (not the model index) 4859 * to get the value. 4860 * 4861 * @return the cell value, as returned by model. 4862 */ getValueAt(int row, int column)4863 public Object getValueAt(int row, int column) 4864 { 4865 return dataModel.getValueAt(row, convertColumnIndexToModel(column)); 4866 } 4867 4868 /** 4869 * Set value for the cell at the given position. The modified cell is 4870 * repainted. 4871 * 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 4875 */ setValueAt(Object value, int row, int column)4876 public void setValueAt(Object value, int row, int column) 4877 { 4878 dataModel.setValueAt(value, row, convertColumnIndexToModel(column)); 4879 4880 repaint(getCellRect(row, column, true)); 4881 } 4882 4883 /** 4884 * Get table column with the given identified. 4885 * 4886 * @param identifier the column identifier 4887 * 4888 * @return the table column with this identifier 4889 * 4890 * @throws IllegalArgumentException if <code>identifier</code> is 4891 * <code>null</code> or there is no column with that identifier. 4892 * 4893 * @see TableColumn#setIdentifier(Object) 4894 */ getColumn(Object identifier)4895 public TableColumn getColumn(Object identifier) 4896 { 4897 return columnModel.getColumn(columnModel.getColumnIndex(identifier)); 4898 } 4899 4900 /** 4901 * Returns <code>true</code> if the specified cell is editable, and 4902 * <code>false</code> otherwise. 4903 * 4904 * @param row the row index. 4905 * @param column the column index. 4906 * 4907 * @return true if the cell is editable, false otherwise. 4908 */ isCellEditable(int row, int column)4909 public boolean isCellEditable(int row, int column) 4910 { 4911 return dataModel.isCellEditable(row, convertColumnIndexToModel(column)); 4912 } 4913 4914 /** 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. 4918 * 4919 * @see #setAutoCreateColumnsFromModel(boolean) 4920 */ createDefaultColumnsFromModel()4921 public void createDefaultColumnsFromModel() 4922 { 4923 assert columnModel != null : "The columnModel must not be null."; 4924 4925 // remove existing columns 4926 int columnIndex = columnModel.getColumnCount() - 1; 4927 while (columnIndex >= 0) 4928 { 4929 columnModel.removeColumn(columnModel.getColumn(columnIndex)); 4930 columnIndex--; 4931 } 4932 4933 // add new columns to match the TableModel 4934 int columnCount = dataModel.getColumnCount(); 4935 for (int c = 0; c < columnCount; c++) 4936 { 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); 4942 } 4943 } 4944 changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend)4945 public void changeSelection (int rowIndex, int columnIndex, boolean toggle, boolean extend) 4946 { 4947 if (toggle && extend) 4948 { 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); 4953 } 4954 else if (toggle) 4955 { 4956 // Toggle the state of the specified cell 4957 if (isCellSelected(rowIndex,columnIndex)) 4958 { 4959 selectionModel.removeSelectionInterval(rowIndex,rowIndex); 4960 getColumnModel().getSelectionModel().removeSelectionInterval(columnIndex,columnIndex); 4961 } 4962 else 4963 { 4964 selectionModel.addSelectionInterval(rowIndex,rowIndex); 4965 getColumnModel().getSelectionModel().addSelectionInterval(columnIndex,columnIndex); 4966 } 4967 } 4968 else if (extend) 4969 { 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); 4974 } 4975 else 4976 { 4977 // Clear the previous selection and ensure the new cell 4978 // is selected 4979 selectionModel.clearSelection(); 4980 selectionModel.setSelectionInterval(rowIndex,rowIndex); 4981 getColumnModel().getSelectionModel().clearSelection(); 4982 getColumnModel().getSelectionModel().setSelectionInterval(columnIndex, columnIndex); 4983 4984 4985 } 4986 } 4987 4988 /** 4989 * Programmatically starts editing the specified cell. 4990 * 4991 * @param row the row of the cell to edit. 4992 * @param column the column of the cell to edit. 4993 */ editCellAt(int row, int column)4994 public boolean editCellAt(int row, int column) 4995 { 4996 // Complete the previous editing session, if still active. 4997 if (isEditing()) 4998 editingStopped(new ChangeEvent("editingStopped")); 4999 5000 TableCellEditor editor = getCellEditor(row, column); 5001 5002 // The boolean values are inverted by the single click without the 5003 // real editing session. 5004 if (editor == booleanInvertingEditor && isCellEditable(row, column)) 5005 { 5006 if (Boolean.TRUE.equals(getValueAt(row, column))) 5007 setValueAt(Boolean.FALSE, row, column); 5008 else 5009 setValueAt(Boolean.TRUE, row, column); 5010 return false; 5011 } 5012 else 5013 { 5014 editingRow = row; 5015 editingColumn = column; 5016 5017 setCellEditor(editor); 5018 editorComp = prepareEditor(cellEditor, row, column); 5019 5020 // Remove the previous editor components, if present. Only one 5021 // editor component at time is allowed in the table. 5022 removeAll(); 5023 add(editorComp); 5024 moveToCellBeingEdited(editorComp); 5025 scrollRectToVisible(editorComp.getBounds()); 5026 editorComp.requestFocusInWindow(); 5027 5028 // Deliver the should select event. 5029 return editor.shouldSelectCell(null); 5030 } 5031 } 5032 5033 /** 5034 * Move the given component under the cell being edited. 5035 * The table must be in the editing mode. 5036 * 5037 * @param component the component to move. 5038 */ moveToCellBeingEdited(Component component)5039 private void moveToCellBeingEdited(Component component) 5040 { 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. 5045 r.x -= 1; 5046 r.y -= 1; 5047 r.width += 1; 5048 r.height += 1; 5049 component.setBounds(r); 5050 } 5051 5052 /** 5053 * Programmatically starts editing the specified cell. 5054 * 5055 * @param row the row of the cell to edit. 5056 * @param column the column of the cell to edit. 5057 */ editCellAt(int row, int column, EventObject e)5058 public boolean editCellAt (int row, int column, EventObject e) 5059 { 5060 return editCellAt(row, column); 5061 } 5062 5063 /** 5064 * Discards the editor object. 5065 */ removeEditor()5066 public void removeEditor() 5067 { 5068 editingStopped(new ChangeEvent(this)); 5069 } 5070 5071 /** 5072 * Prepares the editor by querying for the value and selection state of the 5073 * cell at (row, column). 5074 * 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 5079 */ prepareEditor(TableCellEditor editor, int row, int column)5080 public Component prepareEditor (TableCellEditor editor, int row, int column) 5081 { 5082 return editor.getTableCellEditorComponent 5083 (this, getValueAt(row, column), isCellSelected(row, column), row, column); 5084 } 5085 5086 /** 5087 * This revalidates the <code>JTable</code> and queues a repaint. 5088 */ resizeAndRepaint()5089 protected void resizeAndRepaint() 5090 { 5091 revalidate(); 5092 repaint(); 5093 } 5094 5095 /** 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. 5100 * 5101 * @param value the value to set 5102 * 5103 * @since 1.4 5104 */ setSurrendersFocusOnKeystroke(boolean value)5105 public void setSurrendersFocusOnKeystroke(boolean value) 5106 { 5107 // TODO: Implement functionality of this property (in UI impl). 5108 surrendersFocusOnKeystroke = value; 5109 } 5110 5111 /** 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. 5116 * 5117 * @return whether cell editors of this table should receive keyboard focus 5118 * when the editor is activated by a keystroke 5119 * 5120 * @since 1.4 5121 */ getSurrendersFocusOnKeystroke()5122 public boolean getSurrendersFocusOnKeystroke() 5123 { 5124 // TODO: Implement functionality of this property (in UI impl). 5125 return surrendersFocusOnKeystroke; 5126 } 5127 5128 /** 5129 * Helper method for 5130 * {@link LookAndFeel#installProperty(JComponent, String, Object)}. 5131 * 5132 * @param propertyName the name of the property 5133 * @param value the value of the property 5134 * 5135 * @throws IllegalArgumentException if the specified property cannot be set 5136 * by this method 5137 * @throws ClassCastException if the property value does not match the 5138 * property type 5139 * @throws NullPointerException if <code>c</code> or 5140 * <code>propertyValue</code> is <code>null</code> 5141 */ setUIProperty(String propertyName, Object value)5142 void setUIProperty(String propertyName, Object value) 5143 { 5144 if (propertyName.equals("rowHeight")) 5145 { 5146 if (! clientRowHeightSet) 5147 { 5148 setRowHeight(((Integer) value).intValue()); 5149 clientRowHeightSet = false; 5150 } 5151 } 5152 else 5153 { 5154 super.setUIProperty(propertyName, value); 5155 } 5156 } 5157 } 5158