1 /* 2 * Copyright (c) 1997, 2014, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.swing; 27 28 import java.util.*; 29 30 import java.applet.Applet; 31 import java.awt.*; 32 import java.awt.event.*; 33 import java.awt.print.*; 34 35 import java.beans.*; 36 37 import java.io.ObjectOutputStream; 38 import java.io.ObjectInputStream; 39 import java.io.IOException; 40 import java.io.InvalidObjectException; 41 42 import javax.accessibility.*; 43 44 import javax.swing.event.*; 45 import javax.swing.plaf.*; 46 import javax.swing.table.*; 47 import javax.swing.border.*; 48 49 import java.text.NumberFormat; 50 import java.text.DateFormat; 51 import java.text.MessageFormat; 52 53 import javax.print.attribute.*; 54 import javax.print.PrintService; 55 import sun.reflect.misc.ReflectUtil; 56 57 import sun.swing.SwingUtilities2; 58 import sun.swing.SwingUtilities2.Section; 59 import static sun.swing.SwingUtilities2.Section.*; 60 import sun.swing.PrintingStatus; 61 62 /** 63 * The <code>JTable</code> is used to display and edit regular two-dimensional tables 64 * of cells. 65 * See <a href="http://docs.oracle.com/javase/tutorial/uiswing/components/table.html">How to Use Tables</a> 66 * in <em>The Java Tutorial</em> 67 * for task-oriented documentation and examples of using <code>JTable</code>. 68 * 69 * <p> 70 * The <code>JTable</code> has many 71 * facilities that make it possible to customize its rendering and editing 72 * but provides defaults for these features so that simple tables can be 73 * set up easily. For example, to set up a table with 10 rows and 10 74 * columns of numbers: 75 * 76 * <pre> 77 * TableModel dataModel = new AbstractTableModel() { 78 * public int getColumnCount() { return 10; } 79 * public int getRowCount() { return 10;} 80 * public Object getValueAt(int row, int col) { return new Integer(row*col); } 81 * }; 82 * JTable table = new JTable(dataModel); 83 * JScrollPane scrollpane = new JScrollPane(table); 84 * </pre> 85 * <p> 86 * {@code JTable}s are typically placed inside of a {@code JScrollPane}. By 87 * default, a {@code JTable} will adjust its width such that 88 * a horizontal scrollbar is unnecessary. To allow for a horizontal scrollbar, 89 * invoke {@link #setAutoResizeMode} with {@code AUTO_RESIZE_OFF}. 90 * Note that if you wish to use a <code>JTable</code> in a standalone 91 * view (outside of a <code>JScrollPane</code>) and want the header 92 * displayed, you can get it using {@link #getTableHeader} and 93 * display it separately. 94 * <p> 95 * To enable sorting and filtering of rows, use a 96 * {@code RowSorter}. 97 * You can set up a row sorter in either of two ways: 98 * <ul> 99 * <li>Directly set the {@code RowSorter}. For example: 100 * {@code table.setRowSorter(new TableRowSorter(model))}. 101 * <li>Set the {@code autoCreateRowSorter} 102 * property to {@code true}, so that the {@code JTable} 103 * creates a {@code RowSorter} for 104 * you. For example: {@code setAutoCreateRowSorter(true)}. 105 * </ul> 106 * <p> 107 * When designing applications that use the <code>JTable</code> it is worth paying 108 * close attention to the data structures that will represent the table's data. 109 * The <code>DefaultTableModel</code> is a model implementation that 110 * uses a <code>Vector</code> of <code>Vector</code>s of <code>Object</code>s to 111 * store the cell values. As well as copying the data from an 112 * application into the <code>DefaultTableModel</code>, 113 * it is also possible to wrap the data in the methods of the 114 * <code>TableModel</code> interface so that the data can be passed to the 115 * <code>JTable</code> directly, as in the example above. This often results 116 * in more efficient applications because the model is free to choose the 117 * internal representation that best suits the data. 118 * A good rule of thumb for deciding whether to use the <code>AbstractTableModel</code> 119 * or the <code>DefaultTableModel</code> is to use the <code>AbstractTableModel</code> 120 * as the base class for creating subclasses and the <code>DefaultTableModel</code> 121 * when subclassing is not required. 122 * <p> 123 * The "TableExample" directory in the demo area of the source distribution 124 * gives a number of complete examples of <code>JTable</code> usage, 125 * covering how the <code>JTable</code> can be used to provide an 126 * editable view of data taken from a database and how to modify 127 * the columns in the display to use specialized renderers and editors. 128 * <p> 129 * The <code>JTable</code> uses integers exclusively to refer to both the rows and the columns 130 * of the model that it displays. The <code>JTable</code> simply takes a tabular range of cells 131 * and uses <code>getValueAt(int, int)</code> to retrieve the 132 * values from the model during painting. It is important to remember that 133 * the column and row indexes returned by various <code>JTable</code> methods 134 * are in terms of the <code>JTable</code> (the view) and are not 135 * necessarily the same indexes used by the model. 136 * <p> 137 * By default, columns may be rearranged in the <code>JTable</code> so that the 138 * view's columns appear in a different order to the columns in the model. 139 * This does not affect the implementation of the model at all: when the 140 * columns are reordered, the <code>JTable</code> maintains the new order of the columns 141 * internally and converts its column indices before querying the model. 142 * <p> 143 * So, when writing a <code>TableModel</code>, it is not necessary to listen for column 144 * reordering events as the model will be queried in its own coordinate 145 * system regardless of what is happening in the view. 146 * In the examples area there is a demonstration of a sorting algorithm making 147 * use of exactly this technique to interpose yet another coordinate system 148 * where the order of the rows is changed, rather than the order of the columns. 149 * <p> 150 * Similarly when using the sorting and filtering functionality 151 * provided by <code>RowSorter</code> the underlying 152 * <code>TableModel</code> does not need to know how to do sorting, 153 * rather <code>RowSorter</code> will handle it. Coordinate 154 * conversions will be necessary when using the row based methods of 155 * <code>JTable</code> with the underlying <code>TableModel</code>. 156 * All of <code>JTable</code>s row based methods are in terms of the 157 * <code>RowSorter</code>, which is not necessarily the same as that 158 * of the underlying <code>TableModel</code>. For example, the 159 * selection is always in terms of <code>JTable</code> so that when 160 * using <code>RowSorter</code> you will need to convert using 161 * <code>convertRowIndexToView</code> or 162 * <code>convertRowIndexToModel</code>. The following shows how to 163 * convert coordinates from <code>JTable</code> to that of the 164 * underlying model: 165 * <pre> 166 * int[] selection = table.getSelectedRows(); 167 * for (int i = 0; i < selection.length; i++) { 168 * selection[i] = table.convertRowIndexToModel(selection[i]); 169 * } 170 * // selection is now in terms of the underlying TableModel 171 * </pre> 172 * <p> 173 * By default if sorting is enabled <code>JTable</code> will persist the 174 * selection and variable row heights in terms of the model on 175 * sorting. For example if row 0, in terms of the underlying model, 176 * is currently selected, after the sort row 0, in terms of the 177 * underlying model will be selected. Visually the selection may 178 * change, but in terms of the underlying model it will remain the 179 * same. The one exception to that is if the model index is no longer 180 * visible or was removed. For example, if row 0 in terms of model 181 * was filtered out the selection will be empty after the sort. 182 * <p> 183 * J2SE 5 adds methods to <code>JTable</code> to provide convenient access to some 184 * common printing needs. Simple new {@link #print()} methods allow for quick 185 * and easy addition of printing support to your application. In addition, a new 186 * {@link #getPrintable} method is available for more advanced printing needs. 187 * <p> 188 * As for all <code>JComponent</code> classes, you can use 189 * {@link InputMap} and {@link ActionMap} to associate an 190 * {@link Action} object with a {@link KeyStroke} and execute the 191 * action under specified conditions. 192 * <p> 193 * <strong>Warning:</strong> Swing is not thread safe. For more 194 * information see <a 195 * href="package-summary.html#threading">Swing's Threading 196 * Policy</a>. 197 * <p> 198 * <strong>Warning:</strong> 199 * Serialized objects of this class will not be compatible with 200 * future Swing releases. The current serialization support is 201 * appropriate for short term storage or RMI between applications running 202 * the same version of Swing. As of 1.4, support for long term storage 203 * of all JavaBeans™ 204 * has been added to the <code>java.beans</code> package. 205 * Please see {@link java.beans.XMLEncoder}. 206 * 207 * 208 * @beaninfo 209 * attribute: isContainer false 210 * description: A component which displays data in a two dimensional grid. 211 * 212 * @author Philip Milne 213 * @author Shannon Hickey (printing support) 214 * @see javax.swing.table.DefaultTableModel 215 * @see javax.swing.table.TableRowSorter 216 * @since 1.2 217 */ 218 /* The first versions of the JTable, contained in Swing-0.1 through 219 * Swing-0.4, were written by Alan Chung. 220 */ 221 @SuppressWarnings("serial") // Same-version serialization only 222 public class JTable extends JComponent implements TableModelListener, Scrollable, 223 TableColumnModelListener, ListSelectionListener, CellEditorListener, 224 Accessible, RowSorterListener 225 { 226 // 227 // Static Constants 228 // 229 230 /** 231 * @see #getUIClassID 232 * @see #readObject 233 */ 234 private static final String uiClassID = "TableUI"; 235 236 /** Do not adjust column widths automatically; use a horizontal scrollbar instead. */ 237 public static final int AUTO_RESIZE_OFF = 0; 238 239 /** When a column is adjusted in the UI, adjust the next column the opposite way. */ 240 public static final int AUTO_RESIZE_NEXT_COLUMN = 1; 241 242 /** During UI adjustment, change subsequent columns to preserve the total width; 243 * this is the default behavior. */ 244 public static final int AUTO_RESIZE_SUBSEQUENT_COLUMNS = 2; 245 246 /** During all resize operations, apply adjustments to the last column only. */ 247 public static final int AUTO_RESIZE_LAST_COLUMN = 3; 248 249 /** During all resize operations, proportionately resize all columns. */ 250 public static final int AUTO_RESIZE_ALL_COLUMNS = 4; 251 252 253 /** 254 * Printing modes, used in printing <code>JTable</code>s. 255 * 256 * @see #print(JTable.PrintMode, MessageFormat, MessageFormat, 257 * boolean, PrintRequestAttributeSet, boolean) 258 * @see #getPrintable 259 * @since 1.5 260 */ 261 public enum PrintMode { 262 263 /** 264 * Printing mode that prints the table at its current size, 265 * spreading both columns and rows across multiple pages if necessary. 266 */ 267 NORMAL, 268 269 /** 270 * Printing mode that scales the output smaller, if necessary, 271 * to fit the table's entire width (and thereby all columns) on each page; 272 * Rows are spread across multiple pages as necessary. 273 */ 274 FIT_WIDTH 275 } 276 277 278 // 279 // Instance Variables 280 // 281 282 /** The <code>TableModel</code> of the table. */ 283 protected TableModel dataModel; 284 285 /** The <code>TableColumnModel</code> of the table. */ 286 protected TableColumnModel columnModel; 287 288 /** The <code>ListSelectionModel</code> of the table, used to keep track of row selections. */ 289 protected ListSelectionModel selectionModel; 290 291 /** The <code>TableHeader</code> working with the table. */ 292 protected JTableHeader tableHeader; 293 294 /** The height in pixels of each row in the table. */ 295 protected int rowHeight; 296 297 /** The height in pixels of the margin between the cells in each row. */ 298 protected int rowMargin; 299 300 /** The color of the grid. */ 301 protected Color gridColor; 302 303 /** The table draws horizontal lines between cells if <code>showHorizontalLines</code> is true. */ 304 protected boolean showHorizontalLines; 305 306 /** The table draws vertical lines between cells if <code>showVerticalLines</code> is true. */ 307 protected boolean showVerticalLines; 308 309 /** 310 * Determines if the table automatically resizes the 311 * width of the table's columns to take up the entire width of the 312 * table, and how it does the resizing. 313 */ 314 protected int autoResizeMode; 315 316 /** 317 * The table will query the <code>TableModel</code> to build the default 318 * set of columns if this is true. 319 */ 320 protected boolean autoCreateColumnsFromModel; 321 322 /** Used by the <code>Scrollable</code> interface to determine the initial visible area. */ 323 protected Dimension preferredViewportSize; 324 325 /** True if row selection is allowed in this table. */ 326 protected boolean rowSelectionAllowed; 327 328 /** 329 * Obsolete as of Java 2 platform v1.3. Please use the 330 * <code>rowSelectionAllowed</code> property and the 331 * <code>columnSelectionAllowed</code> property of the 332 * <code>columnModel</code> instead. Or use the 333 * method <code>getCellSelectionEnabled</code>. 334 */ 335 /* 336 * If true, both a row selection and a column selection 337 * can be non-empty at the same time, the selected cells are the 338 * the cells whose row and column are both selected. 339 */ 340 protected boolean cellSelectionEnabled; 341 342 /** If editing, the <code>Component</code> that is handling the editing. */ 343 transient protected Component editorComp; 344 345 /** 346 * The active cell editor object, that overwrites the screen real estate 347 * occupied by the current cell and allows the user to change its contents. 348 * {@code null} if the table isn't currently editing. 349 */ 350 transient protected TableCellEditor cellEditor; 351 352 /** Identifies the column of the cell being edited. */ 353 transient protected int editingColumn; 354 355 /** Identifies the row of the cell being edited. */ 356 transient protected int editingRow; 357 358 /** 359 * A table of objects that display the contents of a cell, 360 * indexed by class as declared in <code>getColumnClass</code> 361 * in the <code>TableModel</code> interface. 362 */ 363 transient protected Hashtable<Object, Object> defaultRenderersByColumnClass; 364 // Logicaly, the above is a Hashtable<Class<?>, TableCellRenderer>. 365 // It is declared otherwise to accomodate using UIDefaults. 366 367 /** 368 * A table of objects that display and edit the contents of a cell, 369 * indexed by class as declared in <code>getColumnClass</code> 370 * in the <code>TableModel</code> interface. 371 */ 372 transient protected Hashtable<Object, Object> defaultEditorsByColumnClass; 373 // Logicaly, the above is a Hashtable<Class<?>, TableCellEditor>. 374 // It is declared otherwise to accomodate using UIDefaults. 375 376 /** The foreground color of selected cells. */ 377 protected Color selectionForeground; 378 379 /** The background color of selected cells. */ 380 protected Color selectionBackground; 381 382 // 383 // Private state 384 // 385 386 // WARNING: If you directly access this field you should also change the 387 // SortManager.modelRowSizes field as well. 388 private SizeSequence rowModel; 389 private boolean dragEnabled; 390 private boolean surrendersFocusOnKeystroke; 391 private PropertyChangeListener editorRemover = null; 392 /** 393 * The last value of getValueIsAdjusting from the column selection models 394 * columnSelectionChanged notification. Used to test if a repaint is 395 * needed. 396 */ 397 private boolean columnSelectionAdjusting; 398 /** 399 * The last value of getValueIsAdjusting from the row selection models 400 * valueChanged notification. Used to test if a repaint is needed. 401 */ 402 private boolean rowSelectionAdjusting; 403 404 /** 405 * To communicate errors between threads during printing. 406 */ 407 private Throwable printError; 408 409 /** 410 * True when setRowHeight(int) has been invoked. 411 */ 412 private boolean isRowHeightSet; 413 414 /** 415 * If true, on a sort the selection is reset. 416 */ 417 private boolean updateSelectionOnSort; 418 419 /** 420 * Information used in sorting. 421 */ 422 private transient SortManager sortManager; 423 424 /** 425 * If true, when sorterChanged is invoked it's value is ignored. 426 */ 427 private boolean ignoreSortChange; 428 429 /** 430 * Whether or not sorterChanged has been invoked. 431 */ 432 private boolean sorterChanged; 433 434 /** 435 * If true, any time the model changes a new RowSorter is set. 436 */ 437 private boolean autoCreateRowSorter; 438 439 /** 440 * Whether or not the table always fills the viewport height. 441 * @see #setFillsViewportHeight 442 * @see #getScrollableTracksViewportHeight 443 */ 444 private boolean fillsViewportHeight; 445 446 /** 447 * The drop mode for this component. 448 */ 449 private DropMode dropMode = DropMode.USE_SELECTION; 450 451 /** 452 * The drop location. 453 */ 454 private transient DropLocation dropLocation; 455 456 /** 457 * A subclass of <code>TransferHandler.DropLocation</code> representing 458 * a drop location for a <code>JTable</code>. 459 * 460 * @see #getDropLocation 461 * @since 1.6 462 */ 463 public static final class DropLocation extends TransferHandler.DropLocation { 464 private final int row; 465 private final int col; 466 private final boolean isInsertRow; 467 private final boolean isInsertCol; 468 469 private DropLocation(Point p, int row, int col, 470 boolean isInsertRow, boolean isInsertCol) { 471 472 super(p); 473 this.row = row; 474 this.col = col; 475 this.isInsertRow = isInsertRow; 476 this.isInsertCol = isInsertCol; 477 } 478 479 /** 480 * Returns the row index where a dropped item should be placed in the 481 * table. Interpretation of the value depends on the return of 482 * <code>isInsertRow()</code>. If that method returns 483 * <code>true</code> this value indicates the index where a new 484 * row should be inserted. Otherwise, it represents the value 485 * of an existing row on which the data was dropped. This index is 486 * in terms of the view. 487 * <p> 488 * <code>-1</code> indicates that the drop occurred over empty space, 489 * and no row could be calculated. 490 * 491 * @return the drop row 492 */ 493 public int getRow() { 494 return row; 495 } 496 497 /** 498 * Returns the column index where a dropped item should be placed in the 499 * table. Interpretation of the value depends on the return of 500 * <code>isInsertColumn()</code>. If that method returns 501 * <code>true</code> this value indicates the index where a new 502 * column should be inserted. Otherwise, it represents the value 503 * of an existing column on which the data was dropped. This index is 504 * in terms of the view. 505 * <p> 506 * <code>-1</code> indicates that the drop occurred over empty space, 507 * and no column could be calculated. 508 * 509 * @return the drop row 510 */ 511 public int getColumn() { 512 return col; 513 } 514 515 /** 516 * Returns whether or not this location represents an insert 517 * of a row. 518 * 519 * @return whether or not this is an insert row 520 */ 521 public boolean isInsertRow() { 522 return isInsertRow; 523 } 524 525 /** 526 * Returns whether or not this location represents an insert 527 * of a column. 528 * 529 * @return whether or not this is an insert column 530 */ 531 public boolean isInsertColumn() { 532 return isInsertCol; 533 } 534 535 /** 536 * Returns a string representation of this drop location. 537 * This method is intended to be used for debugging purposes, 538 * and the content and format of the returned string may vary 539 * between implementations. 540 * 541 * @return a string representation of this drop location 542 */ 543 public String toString() { 544 return getClass().getName() 545 + "[dropPoint=" + getDropPoint() + "," 546 + "row=" + row + "," 547 + "column=" + col + "," 548 + "insertRow=" + isInsertRow + "," 549 + "insertColumn=" + isInsertCol + "]"; 550 } 551 } 552 553 // 554 // Constructors 555 // 556 557 /** 558 * Constructs a default <code>JTable</code> that is initialized with a default 559 * data model, a default column model, and a default selection 560 * model. 561 * 562 * @see #createDefaultDataModel 563 * @see #createDefaultColumnModel 564 * @see #createDefaultSelectionModel 565 */ 566 public JTable() { 567 this(null, null, null); 568 } 569 570 /** 571 * Constructs a <code>JTable</code> that is initialized with 572 * <code>dm</code> as the data model, a default column model, 573 * and a default selection model. 574 * 575 * @param dm the data model for the table 576 * @see #createDefaultColumnModel 577 * @see #createDefaultSelectionModel 578 */ 579 public JTable(TableModel dm) { 580 this(dm, null, null); 581 } 582 583 /** 584 * Constructs a <code>JTable</code> that is initialized with 585 * <code>dm</code> as the data model, <code>cm</code> 586 * as the column model, and a default selection model. 587 * 588 * @param dm the data model for the table 589 * @param cm the column model for the table 590 * @see #createDefaultSelectionModel 591 */ 592 public JTable(TableModel dm, TableColumnModel cm) { 593 this(dm, cm, null); 594 } 595 596 /** 597 * Constructs a <code>JTable</code> that is initialized with 598 * <code>dm</code> as the data model, <code>cm</code> as the 599 * column model, and <code>sm</code> as the selection model. 600 * If any of the parameters are <code>null</code> this method 601 * will initialize the table with the corresponding default model. 602 * The <code>autoCreateColumnsFromModel</code> flag is set to false 603 * if <code>cm</code> is non-null, otherwise it is set to true 604 * and the column model is populated with suitable 605 * <code>TableColumns</code> for the columns in <code>dm</code>. 606 * 607 * @param dm the data model for the table 608 * @param cm the column model for the table 609 * @param sm the row selection model for the table 610 * @see #createDefaultDataModel 611 * @see #createDefaultColumnModel 612 * @see #createDefaultSelectionModel 613 */ 614 public JTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) { 615 super(); 616 setLayout(null); 617 618 setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS, 619 JComponent.getManagingFocusForwardTraversalKeys()); 620 setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS, 621 JComponent.getManagingFocusBackwardTraversalKeys()); 622 if (cm == null) { 623 cm = createDefaultColumnModel(); 624 autoCreateColumnsFromModel = true; 625 } 626 setColumnModel(cm); 627 628 if (sm == null) { 629 sm = createDefaultSelectionModel(); 630 } 631 setSelectionModel(sm); 632 633 // Set the model last, that way if the autoCreatColumnsFromModel has 634 // been set above, we will automatically populate an empty columnModel 635 // with suitable columns for the new model. 636 if (dm == null) { 637 dm = createDefaultDataModel(); 638 } 639 setModel(dm); 640 641 initializeLocalVars(); 642 updateUI(); 643 } 644 645 /** 646 * Constructs a <code>JTable</code> with <code>numRows</code> 647 * and <code>numColumns</code> of empty cells using 648 * <code>DefaultTableModel</code>. The columns will have 649 * names of the form "A", "B", "C", etc. 650 * 651 * @param numRows the number of rows the table holds 652 * @param numColumns the number of columns the table holds 653 * @see javax.swing.table.DefaultTableModel 654 */ 655 public JTable(int numRows, int numColumns) { 656 this(new DefaultTableModel(numRows, numColumns)); 657 } 658 659 /** 660 * Constructs a <code>JTable</code> to display the values in the 661 * <code>Vector</code> of <code>Vectors</code>, <code>rowData</code>, 662 * with column names, <code>columnNames</code>. The 663 * <code>Vectors</code> contained in <code>rowData</code> 664 * should contain the values for that row. In other words, 665 * the value of the cell at row 1, column 5 can be obtained 666 * with the following code: 667 * 668 * <pre>((Vector)rowData.elementAt(1)).elementAt(5);</pre> 669 * 670 * @param rowData the data for the new table 671 * @param columnNames names of each column 672 */ 673 @SuppressWarnings("rawtypes") 674 public JTable(Vector<? extends Vector> rowData, Vector<?> columnNames) { 675 this(new DefaultTableModel(rowData, columnNames)); 676 } 677 678 /** 679 * Constructs a <code>JTable</code> to display the values in the two dimensional array, 680 * <code>rowData</code>, with column names, <code>columnNames</code>. 681 * <code>rowData</code> is an array of rows, so the value of the cell at row 1, 682 * column 5 can be obtained with the following code: 683 * 684 * <pre> rowData[1][5]; </pre> 685 * <p> 686 * All rows must be of the same length as <code>columnNames</code>. 687 * 688 * @param rowData the data for the new table 689 * @param columnNames names of each column 690 */ 691 public JTable(final Object[][] rowData, final Object[] columnNames) { 692 this(new AbstractTableModel() { 693 public String getColumnName(int column) { return columnNames[column].toString(); } 694 public int getRowCount() { return rowData.length; } 695 public int getColumnCount() { return columnNames.length; } 696 public Object getValueAt(int row, int col) { return rowData[row][col]; } 697 public boolean isCellEditable(int row, int column) { return true; } 698 public void setValueAt(Object value, int row, int col) { 699 rowData[row][col] = value; 700 fireTableCellUpdated(row, col); 701 } 702 }); 703 } 704 705 /** 706 * Calls the <code>configureEnclosingScrollPane</code> method. 707 * 708 * @see #configureEnclosingScrollPane 709 */ 710 public void addNotify() { 711 super.addNotify(); 712 configureEnclosingScrollPane(); 713 } 714 715 /** 716 * If this <code>JTable</code> is the <code>viewportView</code> of an enclosing <code>JScrollPane</code> 717 * (the usual situation), configure this <code>ScrollPane</code> by, amongst other things, 718 * installing the table's <code>tableHeader</code> as the <code>columnHeaderView</code> of the scroll pane. 719 * When a <code>JTable</code> is added to a <code>JScrollPane</code> in the usual way, 720 * using <code>new JScrollPane(myTable)</code>, <code>addNotify</code> is 721 * called in the <code>JTable</code> (when the table is added to the viewport). 722 * <code>JTable</code>'s <code>addNotify</code> method in turn calls this method, 723 * which is protected so that this default installation procedure can 724 * be overridden by a subclass. 725 * 726 * @see #addNotify 727 */ 728 protected void configureEnclosingScrollPane() { 729 Container parent = SwingUtilities.getUnwrappedParent(this); 730 if (parent instanceof JViewport) { 731 JViewport port = (JViewport) parent; 732 Container gp = port.getParent(); 733 if (gp instanceof JScrollPane) { 734 JScrollPane scrollPane = (JScrollPane)gp; 735 // Make certain we are the viewPort's view and not, for 736 // example, the rowHeaderView of the scrollPane - 737 // an implementor of fixed columns might do this. 738 JViewport viewport = scrollPane.getViewport(); 739 if (viewport == null || 740 SwingUtilities.getUnwrappedView(viewport) != this) { 741 return; 742 } 743 scrollPane.setColumnHeaderView(getTableHeader()); 744 // configure the scrollpane for any LAF dependent settings 745 configureEnclosingScrollPaneUI(); 746 } 747 } 748 } 749 750 /** 751 * This is a sub-part of configureEnclosingScrollPane() that configures 752 * anything on the scrollpane that may change when the look and feel 753 * changes. It needed to be split out from configureEnclosingScrollPane() so 754 * that it can be called from updateUI() when the LAF changes without 755 * causing the regression found in bug 6687962. This was because updateUI() 756 * is called from the constructor which then caused 757 * configureEnclosingScrollPane() to be called by the constructor which 758 * changes its contract for any subclass that overrides it. So by splitting 759 * it out in this way configureEnclosingScrollPaneUI() can be called both 760 * from configureEnclosingScrollPane() and updateUI() in a safe manor. 761 */ 762 private void configureEnclosingScrollPaneUI() { 763 Container parent = SwingUtilities.getUnwrappedParent(this); 764 if (parent instanceof JViewport) { 765 JViewport port = (JViewport) parent; 766 Container gp = port.getParent(); 767 if (gp instanceof JScrollPane) { 768 JScrollPane scrollPane = (JScrollPane)gp; 769 // Make certain we are the viewPort's view and not, for 770 // example, the rowHeaderView of the scrollPane - 771 // an implementor of fixed columns might do this. 772 JViewport viewport = scrollPane.getViewport(); 773 if (viewport == null || 774 SwingUtilities.getUnwrappedView(viewport) != this) { 775 return; 776 } 777 // scrollPane.getViewport().setBackingStoreEnabled(true); 778 Border border = scrollPane.getBorder(); 779 if (border == null || border instanceof UIResource) { 780 Border scrollPaneBorder = 781 UIManager.getBorder("Table.scrollPaneBorder"); 782 if (scrollPaneBorder != null) { 783 scrollPane.setBorder(scrollPaneBorder); 784 } 785 } 786 // add JScrollBar corner component if available from LAF and not already set by the user 787 Component corner = 788 scrollPane.getCorner(JScrollPane.UPPER_TRAILING_CORNER); 789 if (corner == null || corner instanceof UIResource){ 790 corner = null; 791 try { 792 corner = (Component) UIManager.get( 793 "Table.scrollPaneCornerComponent"); 794 } catch (Exception e) { 795 // just ignore and don't set corner 796 } 797 scrollPane.setCorner(JScrollPane.UPPER_TRAILING_CORNER, 798 corner); 799 } 800 } 801 } 802 } 803 804 /** 805 * Calls the <code>unconfigureEnclosingScrollPane</code> method. 806 * 807 * @see #unconfigureEnclosingScrollPane 808 */ 809 public void removeNotify() { 810 KeyboardFocusManager.getCurrentKeyboardFocusManager(). 811 removePropertyChangeListener("permanentFocusOwner", editorRemover); 812 editorRemover = null; 813 unconfigureEnclosingScrollPane(); 814 super.removeNotify(); 815 } 816 817 /** 818 * Reverses the effect of <code>configureEnclosingScrollPane</code> 819 * by replacing the <code>columnHeaderView</code> of the enclosing 820 * scroll pane with <code>null</code>. <code>JTable</code>'s 821 * <code>removeNotify</code> method calls 822 * this method, which is protected so that this default uninstallation 823 * procedure can be overridden by a subclass. 824 * 825 * @see #removeNotify 826 * @see #configureEnclosingScrollPane 827 * @since 1.3 828 */ 829 protected void unconfigureEnclosingScrollPane() { 830 Container parent = SwingUtilities.getUnwrappedParent(this); 831 if (parent instanceof JViewport) { 832 JViewport port = (JViewport) parent; 833 Container gp = port.getParent(); 834 if (gp instanceof JScrollPane) { 835 JScrollPane scrollPane = (JScrollPane)gp; 836 // Make certain we are the viewPort's view and not, for 837 // example, the rowHeaderView of the scrollPane - 838 // an implementor of fixed columns might do this. 839 JViewport viewport = scrollPane.getViewport(); 840 if (viewport == null || 841 SwingUtilities.getUnwrappedView(viewport) != this) { 842 return; 843 } 844 scrollPane.setColumnHeaderView(null); 845 // remove ScrollPane corner if one was added by the LAF 846 Component corner = 847 scrollPane.getCorner(JScrollPane.UPPER_TRAILING_CORNER); 848 if (corner instanceof UIResource){ 849 scrollPane.setCorner(JScrollPane.UPPER_TRAILING_CORNER, 850 null); 851 } 852 } 853 } 854 } 855 856 void setUIProperty(String propertyName, Object value) { 857 if (propertyName == "rowHeight") { 858 if (!isRowHeightSet) { 859 setRowHeight(((Number)value).intValue()); 860 isRowHeightSet = false; 861 } 862 return; 863 } 864 super.setUIProperty(propertyName, value); 865 } 866 867 // 868 // Static Methods 869 // 870 871 /** 872 * Equivalent to <code>new JScrollPane(aTable)</code>. 873 * 874 * @param aTable a {@code JTable} to be used for the scroll pane 875 * @return a {@code JScrollPane} created using {@code aTable} 876 * @deprecated As of Swing version 1.0.2, 877 * replaced by <code>new JScrollPane(aTable)</code>. 878 */ 879 @Deprecated 880 static public JScrollPane createScrollPaneForTable(JTable aTable) { 881 return new JScrollPane(aTable); 882 } 883 884 // 885 // Table Attributes 886 // 887 888 /** 889 * Sets the <code>tableHeader</code> working with this <code>JTable</code> to <code>newHeader</code>. 890 * It is legal to have a <code>null</code> <code>tableHeader</code>. 891 * 892 * @param tableHeader new tableHeader 893 * @see #getTableHeader 894 * @beaninfo 895 * bound: true 896 * description: The JTableHeader instance which renders the column headers. 897 */ 898 public void setTableHeader(JTableHeader tableHeader) { 899 if (this.tableHeader != tableHeader) { 900 JTableHeader old = this.tableHeader; 901 // Release the old header 902 if (old != null) { 903 old.setTable(null); 904 } 905 this.tableHeader = tableHeader; 906 if (tableHeader != null) { 907 tableHeader.setTable(this); 908 } 909 firePropertyChange("tableHeader", old, tableHeader); 910 } 911 } 912 913 /** 914 * Returns the <code>tableHeader</code> used by this <code>JTable</code>. 915 * 916 * @return the <code>tableHeader</code> used by this table 917 * @see #setTableHeader 918 */ 919 public JTableHeader getTableHeader() { 920 return tableHeader; 921 } 922 923 /** 924 * Sets the height, in pixels, of all cells to <code>rowHeight</code>, 925 * revalidates, and repaints. 926 * The height of the cells will be equal to the row height minus 927 * the row margin. 928 * 929 * @param rowHeight new row height 930 * @exception IllegalArgumentException if <code>rowHeight</code> is 931 * less than 1 932 * @see #getRowHeight 933 * @beaninfo 934 * bound: true 935 * description: The height of the specified row. 936 */ 937 public void setRowHeight(int rowHeight) { 938 if (rowHeight <= 0) { 939 throw new IllegalArgumentException("New row height less than 1"); 940 } 941 int old = this.rowHeight; 942 this.rowHeight = rowHeight; 943 rowModel = null; 944 if (sortManager != null) { 945 sortManager.modelRowSizes = null; 946 } 947 isRowHeightSet = true; 948 resizeAndRepaint(); 949 firePropertyChange("rowHeight", old, rowHeight); 950 } 951 952 /** 953 * Returns the height of a table row, in pixels. 954 * 955 * @return the height in pixels of a table row 956 * @see #setRowHeight 957 */ 958 public int getRowHeight() { 959 return rowHeight; 960 } 961 962 private SizeSequence getRowModel() { 963 if (rowModel == null) { 964 rowModel = new SizeSequence(getRowCount(), getRowHeight()); 965 } 966 return rowModel; 967 } 968 969 /** 970 * Sets the height for <code>row</code> to <code>rowHeight</code>, 971 * revalidates, and repaints. The height of the cells in this row 972 * will be equal to the row height minus the row margin. 973 * 974 * @param row the row whose height is being 975 changed 976 * @param rowHeight new row height, in pixels 977 * @exception IllegalArgumentException if <code>rowHeight</code> is 978 * less than 1 979 * @beaninfo 980 * bound: true 981 * description: The height in pixels of the cells in <code>row</code> 982 * @since 1.3 983 */ 984 public void setRowHeight(int row, int rowHeight) { 985 if (rowHeight <= 0) { 986 throw new IllegalArgumentException("New row height less than 1"); 987 } 988 getRowModel().setSize(row, rowHeight); 989 if (sortManager != null) { 990 sortManager.setViewRowHeight(row, rowHeight); 991 } 992 resizeAndRepaint(); 993 } 994 995 /** 996 * Returns the height, in pixels, of the cells in <code>row</code>. 997 * @param row the row whose height is to be returned 998 * @return the height, in pixels, of the cells in the row 999 * @since 1.3 1000 */ 1001 public int getRowHeight(int row) { 1002 return (rowModel == null) ? getRowHeight() : rowModel.getSize(row); 1003 } 1004 1005 /** 1006 * Sets the amount of empty space between cells in adjacent rows. 1007 * 1008 * @param rowMargin the number of pixels between cells in a row 1009 * @see #getRowMargin 1010 * @beaninfo 1011 * bound: true 1012 * description: The amount of space between cells. 1013 */ 1014 public void setRowMargin(int rowMargin) { 1015 int old = this.rowMargin; 1016 this.rowMargin = rowMargin; 1017 resizeAndRepaint(); 1018 firePropertyChange("rowMargin", old, rowMargin); 1019 } 1020 1021 /** 1022 * Gets the amount of empty space, in pixels, between cells. Equivalent to: 1023 * <code>getIntercellSpacing().height</code>. 1024 * @return the number of pixels between cells in a row 1025 * 1026 * @see #setRowMargin 1027 */ 1028 public int getRowMargin() { 1029 return rowMargin; 1030 } 1031 1032 /** 1033 * Sets the <code>rowMargin</code> and the <code>columnMargin</code> -- 1034 * the height and width of the space between cells -- to 1035 * <code>intercellSpacing</code>. 1036 * 1037 * @param intercellSpacing a <code>Dimension</code> 1038 * specifying the new width 1039 * and height between cells 1040 * @see #getIntercellSpacing 1041 * @beaninfo 1042 * description: The spacing between the cells, 1043 * drawn in the background color of the JTable. 1044 */ 1045 public void setIntercellSpacing(Dimension intercellSpacing) { 1046 // Set the rowMargin here and columnMargin in the TableColumnModel 1047 setRowMargin(intercellSpacing.height); 1048 getColumnModel().setColumnMargin(intercellSpacing.width); 1049 1050 resizeAndRepaint(); 1051 } 1052 1053 /** 1054 * Returns the horizontal and vertical space between cells. 1055 * The default spacing is look and feel dependent. 1056 * 1057 * @return the horizontal and vertical spacing between cells 1058 * @see #setIntercellSpacing 1059 */ 1060 public Dimension getIntercellSpacing() { 1061 return new Dimension(getColumnModel().getColumnMargin(), rowMargin); 1062 } 1063 1064 /** 1065 * Sets the color used to draw grid lines to <code>gridColor</code> and redisplays. 1066 * The default color is look and feel dependent. 1067 * 1068 * @param gridColor the new color of the grid lines 1069 * @exception IllegalArgumentException if <code>gridColor</code> is <code>null</code> 1070 * @see #getGridColor 1071 * @beaninfo 1072 * bound: true 1073 * description: The grid color. 1074 */ 1075 public void setGridColor(Color gridColor) { 1076 if (gridColor == null) { 1077 throw new IllegalArgumentException("New color is null"); 1078 } 1079 Color old = this.gridColor; 1080 this.gridColor = gridColor; 1081 firePropertyChange("gridColor", old, gridColor); 1082 // Redraw 1083 repaint(); 1084 } 1085 1086 /** 1087 * Returns the color used to draw grid lines. 1088 * The default color is look and feel dependent. 1089 * 1090 * @return the color used to draw grid lines 1091 * @see #setGridColor 1092 */ 1093 public Color getGridColor() { 1094 return gridColor; 1095 } 1096 1097 /** 1098 * Sets whether the table draws grid lines around cells. 1099 * If <code>showGrid</code> is true it does; if it is false it doesn't. 1100 * There is no <code>getShowGrid</code> method as this state is held 1101 * in two variables -- <code>showHorizontalLines</code> and <code>showVerticalLines</code> -- 1102 * each of which can be queried independently. 1103 * 1104 * @param showGrid true if table view should draw grid lines 1105 * 1106 * @see #setShowVerticalLines 1107 * @see #setShowHorizontalLines 1108 * @beaninfo 1109 * description: The color used to draw the grid lines. 1110 */ 1111 public void setShowGrid(boolean showGrid) { 1112 setShowHorizontalLines(showGrid); 1113 setShowVerticalLines(showGrid); 1114 1115 // Redraw 1116 repaint(); 1117 } 1118 1119 /** 1120 * Sets whether the table draws horizontal lines between cells. 1121 * If <code>showHorizontalLines</code> is true it does; if it is false it doesn't. 1122 * 1123 * @param showHorizontalLines true if table view should draw horizontal lines 1124 * @see #getShowHorizontalLines 1125 * @see #setShowGrid 1126 * @see #setShowVerticalLines 1127 * @beaninfo 1128 * bound: true 1129 * description: Whether horizontal lines should be drawn in between the cells. 1130 */ 1131 public void setShowHorizontalLines(boolean showHorizontalLines) { 1132 boolean old = this.showHorizontalLines; 1133 this.showHorizontalLines = showHorizontalLines; 1134 firePropertyChange("showHorizontalLines", old, showHorizontalLines); 1135 1136 // Redraw 1137 repaint(); 1138 } 1139 1140 /** 1141 * Sets whether the table draws vertical lines between cells. 1142 * If <code>showVerticalLines</code> is true it does; if it is false it doesn't. 1143 * 1144 * @param showVerticalLines true if table view should draw vertical lines 1145 * @see #getShowVerticalLines 1146 * @see #setShowGrid 1147 * @see #setShowHorizontalLines 1148 * @beaninfo 1149 * bound: true 1150 * description: Whether vertical lines should be drawn in between the cells. 1151 */ 1152 public void setShowVerticalLines(boolean showVerticalLines) { 1153 boolean old = this.showVerticalLines; 1154 this.showVerticalLines = showVerticalLines; 1155 firePropertyChange("showVerticalLines", old, showVerticalLines); 1156 // Redraw 1157 repaint(); 1158 } 1159 1160 /** 1161 * Returns true if the table draws horizontal lines between cells, false if it 1162 * doesn't. The default value is look and feel dependent. 1163 * 1164 * @return true if the table draws horizontal lines between cells, false if it 1165 * doesn't 1166 * @see #setShowHorizontalLines 1167 */ 1168 public boolean getShowHorizontalLines() { 1169 return showHorizontalLines; 1170 } 1171 1172 /** 1173 * Returns true if the table draws vertical lines between cells, false if it 1174 * doesn't. The default value is look and feel dependent. 1175 * 1176 * @return true if the table draws vertical lines between cells, false if it 1177 * doesn't 1178 * @see #setShowVerticalLines 1179 */ 1180 public boolean getShowVerticalLines() { 1181 return showVerticalLines; 1182 } 1183 1184 /** 1185 * Sets the table's auto resize mode when the table is resized. For further 1186 * information on how the different resize modes work, see 1187 * {@link #doLayout}. 1188 * 1189 * @param mode One of 5 legal values: 1190 * AUTO_RESIZE_OFF, 1191 * AUTO_RESIZE_NEXT_COLUMN, 1192 * AUTO_RESIZE_SUBSEQUENT_COLUMNS, 1193 * AUTO_RESIZE_LAST_COLUMN, 1194 * AUTO_RESIZE_ALL_COLUMNS 1195 * 1196 * @see #getAutoResizeMode 1197 * @see #doLayout 1198 * @beaninfo 1199 * bound: true 1200 * description: Whether the columns should adjust themselves automatically. 1201 * enum: AUTO_RESIZE_OFF JTable.AUTO_RESIZE_OFF 1202 * AUTO_RESIZE_NEXT_COLUMN JTable.AUTO_RESIZE_NEXT_COLUMN 1203 * AUTO_RESIZE_SUBSEQUENT_COLUMNS JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS 1204 * AUTO_RESIZE_LAST_COLUMN JTable.AUTO_RESIZE_LAST_COLUMN 1205 * AUTO_RESIZE_ALL_COLUMNS JTable.AUTO_RESIZE_ALL_COLUMNS 1206 */ 1207 public void setAutoResizeMode(int mode) { 1208 if (isValidAutoResizeMode(mode)) { 1209 int old = autoResizeMode; 1210 autoResizeMode = mode; 1211 resizeAndRepaint(); 1212 if (tableHeader != null) { 1213 tableHeader.resizeAndRepaint(); 1214 } 1215 firePropertyChange("autoResizeMode", old, autoResizeMode); 1216 } 1217 } 1218 1219 private static boolean isValidAutoResizeMode(int mode) { 1220 return (mode == AUTO_RESIZE_OFF) 1221 || (mode == AUTO_RESIZE_NEXT_COLUMN) 1222 || (mode == AUTO_RESIZE_SUBSEQUENT_COLUMNS) 1223 || (mode == AUTO_RESIZE_LAST_COLUMN) 1224 || (mode == AUTO_RESIZE_ALL_COLUMNS); 1225 } 1226 1227 /** 1228 * Returns the auto resize mode of the table. The default mode 1229 * is AUTO_RESIZE_SUBSEQUENT_COLUMNS. 1230 * 1231 * @return the autoResizeMode of the table 1232 * 1233 * @see #setAutoResizeMode 1234 * @see #doLayout 1235 */ 1236 public int getAutoResizeMode() { 1237 return autoResizeMode; 1238 } 1239 1240 /** 1241 * Sets this table's <code>autoCreateColumnsFromModel</code> flag. 1242 * This method calls <code>createDefaultColumnsFromModel</code> if 1243 * <code>autoCreateColumnsFromModel</code> changes from false to true. 1244 * 1245 * @param autoCreateColumnsFromModel true if <code>JTable</code> should automatically create columns 1246 * @see #getAutoCreateColumnsFromModel 1247 * @see #createDefaultColumnsFromModel 1248 * @beaninfo 1249 * bound: true 1250 * description: Automatically populates the columnModel when a new TableModel is submitted. 1251 */ 1252 public void setAutoCreateColumnsFromModel(boolean autoCreateColumnsFromModel) { 1253 if (this.autoCreateColumnsFromModel != autoCreateColumnsFromModel) { 1254 boolean old = this.autoCreateColumnsFromModel; 1255 this.autoCreateColumnsFromModel = autoCreateColumnsFromModel; 1256 if (autoCreateColumnsFromModel) { 1257 createDefaultColumnsFromModel(); 1258 } 1259 firePropertyChange("autoCreateColumnsFromModel", old, autoCreateColumnsFromModel); 1260 } 1261 } 1262 1263 /** 1264 * Determines whether the table will create default columns from the model. 1265 * If true, <code>setModel</code> will clear any existing columns and 1266 * create new columns from the new model. Also, if the event in 1267 * the <code>tableChanged</code> notification specifies that the 1268 * entire table changed, then the columns will be rebuilt. 1269 * The default is true. 1270 * 1271 * @return the autoCreateColumnsFromModel of the table 1272 * @see #setAutoCreateColumnsFromModel 1273 * @see #createDefaultColumnsFromModel 1274 */ 1275 public boolean getAutoCreateColumnsFromModel() { 1276 return autoCreateColumnsFromModel; 1277 } 1278 1279 /** 1280 * Creates default columns for the table from 1281 * the data model using the <code>getColumnCount</code> method 1282 * defined in the <code>TableModel</code> interface. 1283 * <p> 1284 * Clears any existing columns before creating the 1285 * new columns based on information from the model. 1286 * 1287 * @see #getAutoCreateColumnsFromModel 1288 */ 1289 public void createDefaultColumnsFromModel() { 1290 TableModel m = getModel(); 1291 if (m != null) { 1292 // Remove any current columns 1293 TableColumnModel cm = getColumnModel(); 1294 while (cm.getColumnCount() > 0) { 1295 cm.removeColumn(cm.getColumn(0)); 1296 } 1297 1298 // Create new columns from the data model info 1299 for (int i = 0; i < m.getColumnCount(); i++) { 1300 TableColumn newColumn = new TableColumn(i); 1301 addColumn(newColumn); 1302 } 1303 } 1304 } 1305 1306 /** 1307 * Sets a default cell renderer to be used if no renderer has been set in 1308 * a <code>TableColumn</code>. If renderer is <code>null</code>, 1309 * removes the default renderer for this column class. 1310 * 1311 * @param columnClass set the default cell renderer for this columnClass 1312 * @param renderer default cell renderer to be used for this 1313 * columnClass 1314 * @see #getDefaultRenderer 1315 * @see #setDefaultEditor 1316 */ 1317 public void setDefaultRenderer(Class<?> columnClass, TableCellRenderer renderer) { 1318 if (renderer != null) { 1319 defaultRenderersByColumnClass.put(columnClass, renderer); 1320 } 1321 else { 1322 defaultRenderersByColumnClass.remove(columnClass); 1323 } 1324 } 1325 1326 /** 1327 * Returns the cell renderer to be used when no renderer has been set in 1328 * a <code>TableColumn</code>. During the rendering of cells the renderer is fetched from 1329 * a <code>Hashtable</code> of entries according to the class of the cells in the column. If 1330 * there is no entry for this <code>columnClass</code> the method returns 1331 * the entry for the most specific superclass. The <code>JTable</code> installs entries 1332 * for <code>Object</code>, <code>Number</code>, and <code>Boolean</code>, all of which can be modified 1333 * or replaced. 1334 * 1335 * @param columnClass return the default cell renderer 1336 * for this columnClass 1337 * @return the renderer for this columnClass 1338 * @see #setDefaultRenderer 1339 * @see #getColumnClass 1340 */ 1341 public TableCellRenderer getDefaultRenderer(Class<?> columnClass) { 1342 if (columnClass == null) { 1343 return null; 1344 } 1345 else { 1346 Object renderer = defaultRenderersByColumnClass.get(columnClass); 1347 if (renderer != null) { 1348 return (TableCellRenderer)renderer; 1349 } 1350 else { 1351 Class<?> c = columnClass.getSuperclass(); 1352 if (c == null && columnClass != Object.class) { 1353 c = Object.class; 1354 } 1355 return getDefaultRenderer(c); 1356 } 1357 } 1358 } 1359 1360 /** 1361 * Sets a default cell editor to be used if no editor has been set in 1362 * a <code>TableColumn</code>. If no editing is required in a table, or a 1363 * particular column in a table, uses the <code>isCellEditable</code> 1364 * method in the <code>TableModel</code> interface to ensure that this 1365 * <code>JTable</code> will not start an editor in these columns. 1366 * If editor is <code>null</code>, removes the default editor for this 1367 * column class. 1368 * 1369 * @param columnClass set the default cell editor for this columnClass 1370 * @param editor default cell editor to be used for this columnClass 1371 * @see TableModel#isCellEditable 1372 * @see #getDefaultEditor 1373 * @see #setDefaultRenderer 1374 */ 1375 public void setDefaultEditor(Class<?> columnClass, TableCellEditor editor) { 1376 if (editor != null) { 1377 defaultEditorsByColumnClass.put(columnClass, editor); 1378 } 1379 else { 1380 defaultEditorsByColumnClass.remove(columnClass); 1381 } 1382 } 1383 1384 /** 1385 * Returns the editor to be used when no editor has been set in 1386 * a <code>TableColumn</code>. During the editing of cells the editor is fetched from 1387 * a <code>Hashtable</code> of entries according to the class of the cells in the column. If 1388 * there is no entry for this <code>columnClass</code> the method returns 1389 * the entry for the most specific superclass. The <code>JTable</code> installs entries 1390 * for <code>Object</code>, <code>Number</code>, and <code>Boolean</code>, all of which can be modified 1391 * or replaced. 1392 * 1393 * @param columnClass return the default cell editor for this columnClass 1394 * @return the default cell editor to be used for this columnClass 1395 * @see #setDefaultEditor 1396 * @see #getColumnClass 1397 */ 1398 public TableCellEditor getDefaultEditor(Class<?> columnClass) { 1399 if (columnClass == null) { 1400 return null; 1401 } 1402 else { 1403 Object editor = defaultEditorsByColumnClass.get(columnClass); 1404 if (editor != null) { 1405 return (TableCellEditor)editor; 1406 } 1407 else { 1408 return getDefaultEditor(columnClass.getSuperclass()); 1409 } 1410 } 1411 } 1412 1413 /** 1414 * Turns on or off automatic drag handling. In order to enable automatic 1415 * drag handling, this property should be set to {@code true}, and the 1416 * table's {@code TransferHandler} needs to be {@code non-null}. 1417 * The default value of the {@code dragEnabled} property is {@code false}. 1418 * <p> 1419 * The job of honoring this property, and recognizing a user drag gesture, 1420 * lies with the look and feel implementation, and in particular, the table's 1421 * {@code TableUI}. When automatic drag handling is enabled, most look and 1422 * feels (including those that subclass {@code BasicLookAndFeel}) begin a 1423 * drag and drop operation whenever the user presses the mouse button over 1424 * an item (in single selection mode) or a selection (in other selection 1425 * modes) and then moves the mouse a few pixels. Setting this property to 1426 * {@code true} can therefore have a subtle effect on how selections behave. 1427 * <p> 1428 * If a look and feel is used that ignores this property, you can still 1429 * begin a drag and drop operation by calling {@code exportAsDrag} on the 1430 * table's {@code TransferHandler}. 1431 * 1432 * @param b whether or not to enable automatic drag handling 1433 * @exception HeadlessException if 1434 * <code>b</code> is <code>true</code> and 1435 * <code>GraphicsEnvironment.isHeadless()</code> 1436 * returns <code>true</code> 1437 * @see java.awt.GraphicsEnvironment#isHeadless 1438 * @see #getDragEnabled 1439 * @see #setTransferHandler 1440 * @see TransferHandler 1441 * @since 1.4 1442 * 1443 * @beaninfo 1444 * description: determines whether automatic drag handling is enabled 1445 * bound: false 1446 */ 1447 public void setDragEnabled(boolean b) { 1448 checkDragEnabled(b); 1449 dragEnabled = b; 1450 } 1451 1452 private void checkDragEnabled(boolean b) { 1453 if (b && GraphicsEnvironment.isHeadless()) { 1454 throw new HeadlessException(); 1455 } 1456 } 1457 1458 /** 1459 * Returns whether or not automatic drag handling is enabled. 1460 * 1461 * @return the value of the {@code dragEnabled} property 1462 * @see #setDragEnabled 1463 * @since 1.4 1464 */ 1465 public boolean getDragEnabled() { 1466 return dragEnabled; 1467 } 1468 1469 /** 1470 * Sets the drop mode for this component. For backward compatibility, 1471 * the default for this property is <code>DropMode.USE_SELECTION</code>. 1472 * Usage of one of the other modes is recommended, however, for an 1473 * improved user experience. <code>DropMode.ON</code>, for instance, 1474 * offers similar behavior of showing items as selected, but does so without 1475 * affecting the actual selection in the table. 1476 * <p> 1477 * <code>JTable</code> supports the following drop modes: 1478 * <ul> 1479 * <li><code>DropMode.USE_SELECTION</code></li> 1480 * <li><code>DropMode.ON</code></li> 1481 * <li><code>DropMode.INSERT</code></li> 1482 * <li><code>DropMode.INSERT_ROWS</code></li> 1483 * <li><code>DropMode.INSERT_COLS</code></li> 1484 * <li><code>DropMode.ON_OR_INSERT</code></li> 1485 * <li><code>DropMode.ON_OR_INSERT_ROWS</code></li> 1486 * <li><code>DropMode.ON_OR_INSERT_COLS</code></li> 1487 * </ul> 1488 * <p> 1489 * The drop mode is only meaningful if this component has a 1490 * <code>TransferHandler</code> that accepts drops. 1491 * 1492 * @param dropMode the drop mode to use 1493 * @throws IllegalArgumentException if the drop mode is unsupported 1494 * or <code>null</code> 1495 * @see #getDropMode 1496 * @see #getDropLocation 1497 * @see #setTransferHandler 1498 * @see TransferHandler 1499 * @since 1.6 1500 */ 1501 public final void setDropMode(DropMode dropMode) { 1502 checkDropMode(dropMode); 1503 this.dropMode = dropMode; 1504 } 1505 1506 private static void checkDropMode(DropMode dropMode) { 1507 if (dropMode != null) { 1508 switch (dropMode) { 1509 case USE_SELECTION: 1510 case ON: 1511 case INSERT: 1512 case INSERT_ROWS: 1513 case INSERT_COLS: 1514 case ON_OR_INSERT: 1515 case ON_OR_INSERT_ROWS: 1516 case ON_OR_INSERT_COLS: 1517 return; 1518 } 1519 } 1520 throw new IllegalArgumentException(dropMode 1521 + ": Unsupported drop mode for table"); 1522 } 1523 /** 1524 * Returns the drop mode for this component. 1525 * 1526 * @return the drop mode for this component 1527 * @see #setDropMode 1528 * @since 1.6 1529 */ 1530 public final DropMode getDropMode() { 1531 return dropMode; 1532 } 1533 1534 /** 1535 * Calculates a drop location in this component, representing where a 1536 * drop at the given point should insert data. 1537 * 1538 * @param p the point to calculate a drop location for 1539 * @return the drop location, or <code>null</code> 1540 */ 1541 DropLocation dropLocationForPoint(Point p) { 1542 DropLocation location = null; 1543 1544 int row = rowAtPoint(p); 1545 int col = columnAtPoint(p); 1546 boolean outside = Boolean.TRUE == getClientProperty("Table.isFileList") 1547 && SwingUtilities2.pointOutsidePrefSize(this, row, col, p); 1548 1549 Rectangle rect = getCellRect(row, col, true); 1550 Section xSection, ySection; 1551 boolean between = false; 1552 boolean ltr = getComponentOrientation().isLeftToRight(); 1553 1554 switch(dropMode) { 1555 case USE_SELECTION: 1556 case ON: 1557 if (row == -1 || col == -1 || outside) { 1558 location = new DropLocation(p, -1, -1, false, false); 1559 } else { 1560 location = new DropLocation(p, row, col, false, false); 1561 } 1562 break; 1563 case INSERT: 1564 if (row == -1 && col == -1) { 1565 location = new DropLocation(p, 0, 0, true, true); 1566 break; 1567 } 1568 1569 xSection = SwingUtilities2.liesInHorizontal(rect, p, ltr, true); 1570 1571 if (row == -1) { 1572 if (xSection == LEADING) { 1573 location = new DropLocation(p, getRowCount(), col, true, true); 1574 } else if (xSection == TRAILING) { 1575 location = new DropLocation(p, getRowCount(), col + 1, true, true); 1576 } else { 1577 location = new DropLocation(p, getRowCount(), col, true, false); 1578 } 1579 } else if (xSection == LEADING || xSection == TRAILING) { 1580 ySection = SwingUtilities2.liesInVertical(rect, p, true); 1581 if (ySection == LEADING) { 1582 between = true; 1583 } else if (ySection == TRAILING) { 1584 row++; 1585 between = true; 1586 } 1587 1588 location = new DropLocation(p, row, 1589 xSection == TRAILING ? col + 1 : col, 1590 between, true); 1591 } else { 1592 if (SwingUtilities2.liesInVertical(rect, p, false) == TRAILING) { 1593 row++; 1594 } 1595 1596 location = new DropLocation(p, row, col, true, false); 1597 } 1598 1599 break; 1600 case INSERT_ROWS: 1601 if (row == -1 && col == -1) { 1602 location = new DropLocation(p, -1, -1, false, false); 1603 break; 1604 } 1605 1606 if (row == -1) { 1607 location = new DropLocation(p, getRowCount(), col, true, false); 1608 break; 1609 } 1610 1611 if (SwingUtilities2.liesInVertical(rect, p, false) == TRAILING) { 1612 row++; 1613 } 1614 1615 location = new DropLocation(p, row, col, true, false); 1616 break; 1617 case ON_OR_INSERT_ROWS: 1618 if (row == -1 && col == -1) { 1619 location = new DropLocation(p, -1, -1, false, false); 1620 break; 1621 } 1622 1623 if (row == -1) { 1624 location = new DropLocation(p, getRowCount(), col, true, false); 1625 break; 1626 } 1627 1628 ySection = SwingUtilities2.liesInVertical(rect, p, true); 1629 if (ySection == LEADING) { 1630 between = true; 1631 } else if (ySection == TRAILING) { 1632 row++; 1633 between = true; 1634 } 1635 1636 location = new DropLocation(p, row, col, between, false); 1637 break; 1638 case INSERT_COLS: 1639 if (row == -1) { 1640 location = new DropLocation(p, -1, -1, false, false); 1641 break; 1642 } 1643 1644 if (col == -1) { 1645 location = new DropLocation(p, getColumnCount(), col, false, true); 1646 break; 1647 } 1648 1649 if (SwingUtilities2.liesInHorizontal(rect, p, ltr, false) == TRAILING) { 1650 col++; 1651 } 1652 1653 location = new DropLocation(p, row, col, false, true); 1654 break; 1655 case ON_OR_INSERT_COLS: 1656 if (row == -1) { 1657 location = new DropLocation(p, -1, -1, false, false); 1658 break; 1659 } 1660 1661 if (col == -1) { 1662 location = new DropLocation(p, row, getColumnCount(), false, true); 1663 break; 1664 } 1665 1666 xSection = SwingUtilities2.liesInHorizontal(rect, p, ltr, true); 1667 if (xSection == LEADING) { 1668 between = true; 1669 } else if (xSection == TRAILING) { 1670 col++; 1671 between = true; 1672 } 1673 1674 location = new DropLocation(p, row, col, false, between); 1675 break; 1676 case ON_OR_INSERT: 1677 if (row == -1 && col == -1) { 1678 location = new DropLocation(p, 0, 0, true, true); 1679 break; 1680 } 1681 1682 xSection = SwingUtilities2.liesInHorizontal(rect, p, ltr, true); 1683 1684 if (row == -1) { 1685 if (xSection == LEADING) { 1686 location = new DropLocation(p, getRowCount(), col, true, true); 1687 } else if (xSection == TRAILING) { 1688 location = new DropLocation(p, getRowCount(), col + 1, true, true); 1689 } else { 1690 location = new DropLocation(p, getRowCount(), col, true, false); 1691 } 1692 1693 break; 1694 } 1695 1696 ySection = SwingUtilities2.liesInVertical(rect, p, true); 1697 if (ySection == LEADING) { 1698 between = true; 1699 } else if (ySection == TRAILING) { 1700 row++; 1701 between = true; 1702 } 1703 1704 location = new DropLocation(p, row, 1705 xSection == TRAILING ? col + 1 : col, 1706 between, 1707 xSection != MIDDLE); 1708 1709 break; 1710 default: 1711 assert false : "Unexpected drop mode"; 1712 } 1713 1714 return location; 1715 } 1716 1717 /** 1718 * Called to set or clear the drop location during a DnD operation. 1719 * In some cases, the component may need to use it's internal selection 1720 * temporarily to indicate the drop location. To help facilitate this, 1721 * this method returns and accepts as a parameter a state object. 1722 * This state object can be used to store, and later restore, the selection 1723 * state. Whatever this method returns will be passed back to it in 1724 * future calls, as the state parameter. If it wants the DnD system to 1725 * continue storing the same state, it must pass it back every time. 1726 * Here's how this is used: 1727 * <p> 1728 * Let's say that on the first call to this method the component decides 1729 * to save some state (because it is about to use the selection to show 1730 * a drop index). It can return a state object to the caller encapsulating 1731 * any saved selection state. On a second call, let's say the drop location 1732 * is being changed to something else. The component doesn't need to 1733 * restore anything yet, so it simply passes back the same state object 1734 * to have the DnD system continue storing it. Finally, let's say this 1735 * method is messaged with <code>null</code>. This means DnD 1736 * is finished with this component for now, meaning it should restore 1737 * state. At this point, it can use the state parameter to restore 1738 * said state, and of course return <code>null</code> since there's 1739 * no longer anything to store. 1740 * 1741 * @param location the drop location (as calculated by 1742 * <code>dropLocationForPoint</code>) or <code>null</code> 1743 * if there's no longer a valid drop location 1744 * @param state the state object saved earlier for this component, 1745 * or <code>null</code> 1746 * @param forDrop whether or not the method is being called because an 1747 * actual drop occurred 1748 * @return any saved state for this component, or <code>null</code> if none 1749 */ 1750 Object setDropLocation(TransferHandler.DropLocation location, 1751 Object state, 1752 boolean forDrop) { 1753 1754 Object retVal = null; 1755 DropLocation tableLocation = (DropLocation)location; 1756 1757 if (dropMode == DropMode.USE_SELECTION) { 1758 if (tableLocation == null) { 1759 if (!forDrop && state != null) { 1760 clearSelection(); 1761 1762 int[] rows = ((int[][])state)[0]; 1763 int[] cols = ((int[][])state)[1]; 1764 int[] anchleads = ((int[][])state)[2]; 1765 1766 for (int row : rows) { 1767 addRowSelectionInterval(row, row); 1768 } 1769 1770 for (int col : cols) { 1771 addColumnSelectionInterval(col, col); 1772 } 1773 1774 SwingUtilities2.setLeadAnchorWithoutSelection( 1775 getSelectionModel(), anchleads[1], anchleads[0]); 1776 1777 SwingUtilities2.setLeadAnchorWithoutSelection( 1778 getColumnModel().getSelectionModel(), 1779 anchleads[3], anchleads[2]); 1780 } 1781 } else { 1782 if (dropLocation == null) { 1783 retVal = new int[][]{ 1784 getSelectedRows(), 1785 getSelectedColumns(), 1786 {getAdjustedIndex(getSelectionModel() 1787 .getAnchorSelectionIndex(), true), 1788 getAdjustedIndex(getSelectionModel() 1789 .getLeadSelectionIndex(), true), 1790 getAdjustedIndex(getColumnModel().getSelectionModel() 1791 .getAnchorSelectionIndex(), false), 1792 getAdjustedIndex(getColumnModel().getSelectionModel() 1793 .getLeadSelectionIndex(), false)}}; 1794 } else { 1795 retVal = state; 1796 } 1797 1798 if (tableLocation.getRow() == -1) { 1799 clearSelectionAndLeadAnchor(); 1800 } else { 1801 setRowSelectionInterval(tableLocation.getRow(), 1802 tableLocation.getRow()); 1803 setColumnSelectionInterval(tableLocation.getColumn(), 1804 tableLocation.getColumn()); 1805 } 1806 } 1807 } 1808 1809 DropLocation old = dropLocation; 1810 dropLocation = tableLocation; 1811 firePropertyChange("dropLocation", old, dropLocation); 1812 1813 return retVal; 1814 } 1815 1816 /** 1817 * Returns the location that this component should visually indicate 1818 * as the drop location during a DnD operation over the component, 1819 * or {@code null} if no location is to currently be shown. 1820 * <p> 1821 * This method is not meant for querying the drop location 1822 * from a {@code TransferHandler}, as the drop location is only 1823 * set after the {@code TransferHandler}'s <code>canImport</code> 1824 * has returned and has allowed for the location to be shown. 1825 * <p> 1826 * When this property changes, a property change event with 1827 * name "dropLocation" is fired by the component. 1828 * 1829 * @return the drop location 1830 * @see #setDropMode 1831 * @see TransferHandler#canImport(TransferHandler.TransferSupport) 1832 * @since 1.6 1833 */ 1834 public final DropLocation getDropLocation() { 1835 return dropLocation; 1836 } 1837 1838 /** 1839 * Specifies whether a {@code RowSorter} should be created for the 1840 * table whenever its model changes. 1841 * <p> 1842 * When {@code setAutoCreateRowSorter(true)} is invoked, a {@code 1843 * TableRowSorter} is immediately created and installed on the 1844 * table. While the {@code autoCreateRowSorter} property remains 1845 * {@code true}, every time the model is changed, a new {@code 1846 * TableRowSorter} is created and set as the table's row sorter. 1847 * The default value for the {@code autoCreateRowSorter} 1848 * property is {@code false}. 1849 * 1850 * @param autoCreateRowSorter whether or not a {@code RowSorter} 1851 * should be automatically created 1852 * @see javax.swing.table.TableRowSorter 1853 * @beaninfo 1854 * bound: true 1855 * preferred: true 1856 * description: Whether or not to turn on sorting by default. 1857 * @since 1.6 1858 */ 1859 public void setAutoCreateRowSorter(boolean autoCreateRowSorter) { 1860 boolean oldValue = this.autoCreateRowSorter; 1861 this.autoCreateRowSorter = autoCreateRowSorter; 1862 if (autoCreateRowSorter) { 1863 setRowSorter(new TableRowSorter<TableModel>(getModel())); 1864 } 1865 firePropertyChange("autoCreateRowSorter", oldValue, 1866 autoCreateRowSorter); 1867 } 1868 1869 /** 1870 * Returns {@code true} if whenever the model changes, a new 1871 * {@code RowSorter} should be created and installed 1872 * as the table's sorter; otherwise, returns {@code false}. 1873 * 1874 * @return true if a {@code RowSorter} should be created when 1875 * the model changes 1876 * @since 1.6 1877 */ 1878 public boolean getAutoCreateRowSorter() { 1879 return autoCreateRowSorter; 1880 } 1881 1882 /** 1883 * Specifies whether the selection should be updated after sorting. 1884 * If true, on sorting the selection is reset such that 1885 * the same rows, in terms of the model, remain selected. The default 1886 * is true. 1887 * 1888 * @param update whether or not to update the selection on sorting 1889 * @beaninfo 1890 * bound: true 1891 * expert: true 1892 * description: Whether or not to update the selection on sorting 1893 * @since 1.6 1894 */ 1895 public void setUpdateSelectionOnSort(boolean update) { 1896 if (updateSelectionOnSort != update) { 1897 updateSelectionOnSort = update; 1898 firePropertyChange("updateSelectionOnSort", !update, update); 1899 } 1900 } 1901 1902 /** 1903 * Returns true if the selection should be updated after sorting. 1904 * 1905 * @return whether to update the selection on a sort 1906 * @since 1.6 1907 */ 1908 public boolean getUpdateSelectionOnSort() { 1909 return updateSelectionOnSort; 1910 } 1911 1912 /** 1913 * Sets the <code>RowSorter</code>. <code>RowSorter</code> is used 1914 * to provide sorting and filtering to a <code>JTable</code>. 1915 * <p> 1916 * This method clears the selection and resets any variable row heights. 1917 * <p> 1918 * This method fires a <code>PropertyChangeEvent</code> when appropriate, 1919 * with the property name <code>"rowSorter"</code>. For 1920 * backward-compatibility, this method fires an additional event with the 1921 * property name <code>"sorter"</code>. 1922 * <p> 1923 * If the underlying model of the <code>RowSorter</code> differs from 1924 * that of this <code>JTable</code> undefined behavior will result. 1925 * 1926 * @param sorter the <code>RowSorter</code>; <code>null</code> turns 1927 * sorting off 1928 * @see javax.swing.table.TableRowSorter 1929 * @beaninfo 1930 * bound: true 1931 * description: The table's RowSorter 1932 * @since 1.6 1933 */ 1934 public void setRowSorter(RowSorter<? extends TableModel> sorter) { 1935 RowSorter<? extends TableModel> oldRowSorter = null; 1936 if (sortManager != null) { 1937 oldRowSorter = sortManager.sorter; 1938 sortManager.dispose(); 1939 sortManager = null; 1940 } 1941 rowModel = null; 1942 clearSelectionAndLeadAnchor(); 1943 if (sorter != null) { 1944 sortManager = new SortManager(sorter); 1945 } 1946 resizeAndRepaint(); 1947 firePropertyChange("rowSorter", oldRowSorter, sorter); 1948 firePropertyChange("sorter", oldRowSorter, sorter); 1949 } 1950 1951 /** 1952 * Returns the object responsible for sorting. 1953 * 1954 * @return the object responsible for sorting 1955 * @since 1.6 1956 */ 1957 public RowSorter<? extends TableModel> getRowSorter() { 1958 return (sortManager != null) ? sortManager.sorter : null; 1959 } 1960 1961 // 1962 // Selection methods 1963 // 1964 /** 1965 * Sets the table's selection mode to allow only single selections, a single 1966 * contiguous interval, or multiple intervals. 1967 * <P> 1968 * <b>Note:</b> 1969 * <code>JTable</code> provides all the methods for handling 1970 * column and row selection. When setting states, 1971 * such as <code>setSelectionMode</code>, it not only 1972 * updates the mode for the row selection model but also sets similar 1973 * values in the selection model of the <code>columnModel</code>. 1974 * If you want to have the row and column selection models operating 1975 * in different modes, set them both directly. 1976 * <p> 1977 * Both the row and column selection models for <code>JTable</code> 1978 * default to using a <code>DefaultListSelectionModel</code> 1979 * so that <code>JTable</code> works the same way as the 1980 * <code>JList</code>. See the <code>setSelectionMode</code> method 1981 * in <code>JList</code> for details about the modes. 1982 * 1983 * @param selectionMode the mode used by the row and column selection models 1984 * @see JList#setSelectionMode 1985 * @beaninfo 1986 * description: The selection mode used by the row and column selection models. 1987 * enum: SINGLE_SELECTION ListSelectionModel.SINGLE_SELECTION 1988 * SINGLE_INTERVAL_SELECTION ListSelectionModel.SINGLE_INTERVAL_SELECTION 1989 * MULTIPLE_INTERVAL_SELECTION ListSelectionModel.MULTIPLE_INTERVAL_SELECTION 1990 */ 1991 public void setSelectionMode(int selectionMode) { 1992 clearSelection(); 1993 getSelectionModel().setSelectionMode(selectionMode); 1994 getColumnModel().getSelectionModel().setSelectionMode(selectionMode); 1995 } 1996 1997 /** 1998 * Sets whether the rows in this model can be selected. 1999 * 2000 * @param rowSelectionAllowed true if this model will allow row selection 2001 * @see #getRowSelectionAllowed 2002 * @beaninfo 2003 * bound: true 2004 * attribute: visualUpdate true 2005 * description: If true, an entire row is selected for each selected cell. 2006 */ 2007 public void setRowSelectionAllowed(boolean rowSelectionAllowed) { 2008 boolean old = this.rowSelectionAllowed; 2009 this.rowSelectionAllowed = rowSelectionAllowed; 2010 if (old != rowSelectionAllowed) { 2011 repaint(); 2012 } 2013 firePropertyChange("rowSelectionAllowed", old, rowSelectionAllowed); 2014 } 2015 2016 /** 2017 * Returns true if rows can be selected. 2018 * 2019 * @return true if rows can be selected, otherwise false 2020 * @see #setRowSelectionAllowed 2021 */ 2022 public boolean getRowSelectionAllowed() { 2023 return rowSelectionAllowed; 2024 } 2025 2026 /** 2027 * Sets whether the columns in this model can be selected. 2028 * 2029 * @param columnSelectionAllowed true if this model will allow column selection 2030 * @see #getColumnSelectionAllowed 2031 * @beaninfo 2032 * bound: true 2033 * attribute: visualUpdate true 2034 * description: If true, an entire column is selected for each selected cell. 2035 */ 2036 public void setColumnSelectionAllowed(boolean columnSelectionAllowed) { 2037 boolean old = columnModel.getColumnSelectionAllowed(); 2038 columnModel.setColumnSelectionAllowed(columnSelectionAllowed); 2039 if (old != columnSelectionAllowed) { 2040 repaint(); 2041 } 2042 firePropertyChange("columnSelectionAllowed", old, columnSelectionAllowed); 2043 } 2044 2045 /** 2046 * Returns true if columns can be selected. 2047 * 2048 * @return true if columns can be selected, otherwise false 2049 * @see #setColumnSelectionAllowed 2050 */ 2051 public boolean getColumnSelectionAllowed() { 2052 return columnModel.getColumnSelectionAllowed(); 2053 } 2054 2055 /** 2056 * Sets whether this table allows both a column selection and a 2057 * row selection to exist simultaneously. When set, 2058 * the table treats the intersection of the row and column selection 2059 * models as the selected cells. Override <code>isCellSelected</code> to 2060 * change this default behavior. This method is equivalent to setting 2061 * both the <code>rowSelectionAllowed</code> property and 2062 * <code>columnSelectionAllowed</code> property of the 2063 * <code>columnModel</code> to the supplied value. 2064 * 2065 * @param cellSelectionEnabled true if simultaneous row and column 2066 * selection is allowed 2067 * @see #getCellSelectionEnabled 2068 * @see #isCellSelected 2069 * @beaninfo 2070 * bound: true 2071 * attribute: visualUpdate true 2072 * description: Select a rectangular region of cells rather than 2073 * rows or columns. 2074 */ 2075 public void setCellSelectionEnabled(boolean cellSelectionEnabled) { 2076 setRowSelectionAllowed(cellSelectionEnabled); 2077 setColumnSelectionAllowed(cellSelectionEnabled); 2078 boolean old = this.cellSelectionEnabled; 2079 this.cellSelectionEnabled = cellSelectionEnabled; 2080 firePropertyChange("cellSelectionEnabled", old, cellSelectionEnabled); 2081 } 2082 2083 /** 2084 * Returns true if both row and column selection models are enabled. 2085 * Equivalent to <code>getRowSelectionAllowed() && 2086 * getColumnSelectionAllowed()</code>. 2087 * 2088 * @return true if both row and column selection models are enabled 2089 * 2090 * @see #setCellSelectionEnabled 2091 */ 2092 public boolean getCellSelectionEnabled() { 2093 return getRowSelectionAllowed() && getColumnSelectionAllowed(); 2094 } 2095 2096 /** 2097 * Selects all rows, columns, and cells in the table. 2098 */ 2099 public void selectAll() { 2100 // If I'm currently editing, then I should stop editing 2101 if (isEditing()) { 2102 removeEditor(); 2103 } 2104 if (getRowCount() > 0 && getColumnCount() > 0) { 2105 int oldLead; 2106 int oldAnchor; 2107 ListSelectionModel selModel; 2108 2109 selModel = selectionModel; 2110 selModel.setValueIsAdjusting(true); 2111 oldLead = getAdjustedIndex(selModel.getLeadSelectionIndex(), true); 2112 oldAnchor = getAdjustedIndex(selModel.getAnchorSelectionIndex(), true); 2113 2114 setRowSelectionInterval(0, getRowCount()-1); 2115 2116 // this is done to restore the anchor and lead 2117 SwingUtilities2.setLeadAnchorWithoutSelection(selModel, oldLead, oldAnchor); 2118 2119 selModel.setValueIsAdjusting(false); 2120 2121 selModel = columnModel.getSelectionModel(); 2122 selModel.setValueIsAdjusting(true); 2123 oldLead = getAdjustedIndex(selModel.getLeadSelectionIndex(), false); 2124 oldAnchor = getAdjustedIndex(selModel.getAnchorSelectionIndex(), false); 2125 2126 setColumnSelectionInterval(0, getColumnCount()-1); 2127 2128 // this is done to restore the anchor and lead 2129 SwingUtilities2.setLeadAnchorWithoutSelection(selModel, oldLead, oldAnchor); 2130 2131 selModel.setValueIsAdjusting(false); 2132 } 2133 } 2134 2135 /** 2136 * Deselects all selected columns and rows. 2137 */ 2138 public void clearSelection() { 2139 selectionModel.clearSelection(); 2140 columnModel.getSelectionModel().clearSelection(); 2141 } 2142 2143 private void clearSelectionAndLeadAnchor() { 2144 selectionModel.setValueIsAdjusting(true); 2145 columnModel.getSelectionModel().setValueIsAdjusting(true); 2146 2147 clearSelection(); 2148 2149 selectionModel.setAnchorSelectionIndex(-1); 2150 selectionModel.setLeadSelectionIndex(-1); 2151 columnModel.getSelectionModel().setAnchorSelectionIndex(-1); 2152 columnModel.getSelectionModel().setLeadSelectionIndex(-1); 2153 2154 selectionModel.setValueIsAdjusting(false); 2155 columnModel.getSelectionModel().setValueIsAdjusting(false); 2156 } 2157 2158 private int getAdjustedIndex(int index, boolean row) { 2159 int compare = row ? getRowCount() : getColumnCount(); 2160 return index < compare ? index : -1; 2161 } 2162 2163 private int boundRow(int row) throws IllegalArgumentException { 2164 if (row < 0 || row >= getRowCount()) { 2165 throw new IllegalArgumentException("Row index out of range"); 2166 } 2167 return row; 2168 } 2169 2170 private int boundColumn(int col) { 2171 if (col< 0 || col >= getColumnCount()) { 2172 throw new IllegalArgumentException("Column index out of range"); 2173 } 2174 return col; 2175 } 2176 2177 /** 2178 * Selects the rows from <code>index0</code> to <code>index1</code>, 2179 * inclusive. 2180 * 2181 * @exception IllegalArgumentException if <code>index0</code> or 2182 * <code>index1</code> lie outside 2183 * [0, <code>getRowCount()</code>-1] 2184 * @param index0 one end of the interval 2185 * @param index1 the other end of the interval 2186 */ 2187 public void setRowSelectionInterval(int index0, int index1) { 2188 selectionModel.setSelectionInterval(boundRow(index0), boundRow(index1)); 2189 } 2190 2191 /** 2192 * Selects the columns from <code>index0</code> to <code>index1</code>, 2193 * inclusive. 2194 * 2195 * @exception IllegalArgumentException if <code>index0</code> or 2196 * <code>index1</code> lie outside 2197 * [0, <code>getColumnCount()</code>-1] 2198 * @param index0 one end of the interval 2199 * @param index1 the other end of the interval 2200 */ 2201 public void setColumnSelectionInterval(int index0, int index1) { 2202 columnModel.getSelectionModel().setSelectionInterval(boundColumn(index0), boundColumn(index1)); 2203 } 2204 2205 /** 2206 * Adds the rows from <code>index0</code> to <code>index1</code>, inclusive, to 2207 * the current selection. 2208 * 2209 * @exception IllegalArgumentException if <code>index0</code> or <code>index1</code> 2210 * lie outside [0, <code>getRowCount()</code>-1] 2211 * @param index0 one end of the interval 2212 * @param index1 the other end of the interval 2213 */ 2214 public void addRowSelectionInterval(int index0, int index1) { 2215 selectionModel.addSelectionInterval(boundRow(index0), boundRow(index1)); 2216 } 2217 2218 /** 2219 * Adds the columns from <code>index0</code> to <code>index1</code>, 2220 * inclusive, to the current selection. 2221 * 2222 * @exception IllegalArgumentException if <code>index0</code> or 2223 * <code>index1</code> lie outside 2224 * [0, <code>getColumnCount()</code>-1] 2225 * @param index0 one end of the interval 2226 * @param index1 the other end of the interval 2227 */ 2228 public void addColumnSelectionInterval(int index0, int index1) { 2229 columnModel.getSelectionModel().addSelectionInterval(boundColumn(index0), boundColumn(index1)); 2230 } 2231 2232 /** 2233 * Deselects the rows from <code>index0</code> to <code>index1</code>, inclusive. 2234 * 2235 * @exception IllegalArgumentException if <code>index0</code> or 2236 * <code>index1</code> lie outside 2237 * [0, <code>getRowCount()</code>-1] 2238 * @param index0 one end of the interval 2239 * @param index1 the other end of the interval 2240 */ 2241 public void removeRowSelectionInterval(int index0, int index1) { 2242 selectionModel.removeSelectionInterval(boundRow(index0), boundRow(index1)); 2243 } 2244 2245 /** 2246 * Deselects the columns from <code>index0</code> to <code>index1</code>, inclusive. 2247 * 2248 * @exception IllegalArgumentException if <code>index0</code> or 2249 * <code>index1</code> lie outside 2250 * [0, <code>getColumnCount()</code>-1] 2251 * @param index0 one end of the interval 2252 * @param index1 the other end of the interval 2253 */ 2254 public void removeColumnSelectionInterval(int index0, int index1) { 2255 columnModel.getSelectionModel().removeSelectionInterval(boundColumn(index0), boundColumn(index1)); 2256 } 2257 2258 /** 2259 * Returns the index of the first selected row, -1 if no row is selected. 2260 * @return the index of the first selected row 2261 */ 2262 public int getSelectedRow() { 2263 return selectionModel.getMinSelectionIndex(); 2264 } 2265 2266 /** 2267 * Returns the index of the first selected column, 2268 * -1 if no column is selected. 2269 * @return the index of the first selected column 2270 */ 2271 public int getSelectedColumn() { 2272 return columnModel.getSelectionModel().getMinSelectionIndex(); 2273 } 2274 2275 /** 2276 * Returns the indices of all selected rows. 2277 * 2278 * @return an array of integers containing the indices of all selected rows, 2279 * or an empty array if no row is selected 2280 * @see #getSelectedRow 2281 */ 2282 public int[] getSelectedRows() { 2283 int iMin = selectionModel.getMinSelectionIndex(); 2284 int iMax = selectionModel.getMaxSelectionIndex(); 2285 2286 if ((iMin == -1) || (iMax == -1)) { 2287 return new int[0]; 2288 } 2289 2290 int[] rvTmp = new int[1+ (iMax - iMin)]; 2291 int n = 0; 2292 for(int i = iMin; i <= iMax; i++) { 2293 if (selectionModel.isSelectedIndex(i)) { 2294 rvTmp[n++] = i; 2295 } 2296 } 2297 int[] rv = new int[n]; 2298 System.arraycopy(rvTmp, 0, rv, 0, n); 2299 return rv; 2300 } 2301 2302 /** 2303 * Returns the indices of all selected columns. 2304 * 2305 * @return an array of integers containing the indices of all selected columns, 2306 * or an empty array if no column is selected 2307 * @see #getSelectedColumn 2308 */ 2309 public int[] getSelectedColumns() { 2310 return columnModel.getSelectedColumns(); 2311 } 2312 2313 /** 2314 * Returns the number of selected rows. 2315 * 2316 * @return the number of selected rows, 0 if no rows are selected 2317 */ 2318 public int getSelectedRowCount() { 2319 int iMin = selectionModel.getMinSelectionIndex(); 2320 int iMax = selectionModel.getMaxSelectionIndex(); 2321 int count = 0; 2322 2323 for(int i = iMin; i <= iMax; i++) { 2324 if (selectionModel.isSelectedIndex(i)) { 2325 count++; 2326 } 2327 } 2328 return count; 2329 } 2330 2331 /** 2332 * Returns the number of selected columns. 2333 * 2334 * @return the number of selected columns, 0 if no columns are selected 2335 */ 2336 public int getSelectedColumnCount() { 2337 return columnModel.getSelectedColumnCount(); 2338 } 2339 2340 /** 2341 * Returns true if the specified index is in the valid range of rows, 2342 * and the row at that index is selected. 2343 * 2344 * @param row a row in the row model 2345 * @return true if <code>row</code> is a valid index and the row at 2346 * that index is selected (where 0 is the first row) 2347 */ 2348 public boolean isRowSelected(int row) { 2349 return selectionModel.isSelectedIndex(row); 2350 } 2351 2352 /** 2353 * Returns true if the specified index is in the valid range of columns, 2354 * and the column at that index is selected. 2355 * 2356 * @param column the column in the column model 2357 * @return true if <code>column</code> is a valid index and the column at 2358 * that index is selected (where 0 is the first column) 2359 */ 2360 public boolean isColumnSelected(int column) { 2361 return columnModel.getSelectionModel().isSelectedIndex(column); 2362 } 2363 2364 /** 2365 * Returns true if the specified indices are in the valid range of rows 2366 * and columns and the cell at the specified position is selected. 2367 * @param row the row being queried 2368 * @param column the column being queried 2369 * 2370 * @return true if <code>row</code> and <code>column</code> are valid indices 2371 * and the cell at index <code>(row, column)</code> is selected, 2372 * where the first row and first column are at index 0 2373 */ 2374 public boolean isCellSelected(int row, int column) { 2375 if (!getRowSelectionAllowed() && !getColumnSelectionAllowed()) { 2376 return false; 2377 } 2378 return (!getRowSelectionAllowed() || isRowSelected(row)) && 2379 (!getColumnSelectionAllowed() || isColumnSelected(column)); 2380 } 2381 2382 private void changeSelectionModel(ListSelectionModel sm, int index, 2383 boolean toggle, boolean extend, boolean selected, 2384 int anchor, boolean anchorSelected) { 2385 if (extend) { 2386 if (toggle) { 2387 if (anchorSelected) { 2388 sm.addSelectionInterval(anchor, index); 2389 } else { 2390 sm.removeSelectionInterval(anchor, index); 2391 // this is a Windows-only behavior that we want for file lists 2392 if (Boolean.TRUE == getClientProperty("Table.isFileList")) { 2393 sm.addSelectionInterval(index, index); 2394 sm.setAnchorSelectionIndex(anchor); 2395 } 2396 } 2397 } 2398 else { 2399 sm.setSelectionInterval(anchor, index); 2400 } 2401 } 2402 else { 2403 if (toggle) { 2404 if (selected) { 2405 sm.removeSelectionInterval(index, index); 2406 } 2407 else { 2408 sm.addSelectionInterval(index, index); 2409 } 2410 } 2411 else { 2412 sm.setSelectionInterval(index, index); 2413 } 2414 } 2415 } 2416 2417 /** 2418 * Updates the selection models of the table, depending on the state of the 2419 * two flags: <code>toggle</code> and <code>extend</code>. Most changes 2420 * to the selection that are the result of keyboard or mouse events received 2421 * by the UI are channeled through this method so that the behavior may be 2422 * overridden by a subclass. Some UIs may need more functionality than 2423 * this method provides, such as when manipulating the lead for discontiguous 2424 * selection, and may not call into this method for some selection changes. 2425 * <p> 2426 * This implementation uses the following conventions: 2427 * <ul> 2428 * <li> <code>toggle</code>: <em>false</em>, <code>extend</code>: <em>false</em>. 2429 * Clear the previous selection and ensure the new cell is selected. 2430 * <li> <code>toggle</code>: <em>false</em>, <code>extend</code>: <em>true</em>. 2431 * Extend the previous selection from the anchor to the specified cell, 2432 * clearing all other selections. 2433 * <li> <code>toggle</code>: <em>true</em>, <code>extend</code>: <em>false</em>. 2434 * If the specified cell is selected, deselect it. If it is not selected, select it. 2435 * <li> <code>toggle</code>: <em>true</em>, <code>extend</code>: <em>true</em>. 2436 * Apply the selection state of the anchor to all cells between it and the 2437 * specified cell. 2438 * </ul> 2439 * @param rowIndex affects the selection at <code>row</code> 2440 * @param columnIndex affects the selection at <code>column</code> 2441 * @param toggle see description above 2442 * @param extend if true, extend the current selection 2443 * 2444 * @since 1.3 2445 */ 2446 public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) { 2447 ListSelectionModel rsm = getSelectionModel(); 2448 ListSelectionModel csm = getColumnModel().getSelectionModel(); 2449 2450 int anchorRow = getAdjustedIndex(rsm.getAnchorSelectionIndex(), true); 2451 int anchorCol = getAdjustedIndex(csm.getAnchorSelectionIndex(), false); 2452 2453 boolean anchorSelected = true; 2454 2455 if (anchorRow == -1) { 2456 if (getRowCount() > 0) { 2457 anchorRow = 0; 2458 } 2459 anchorSelected = false; 2460 } 2461 2462 if (anchorCol == -1) { 2463 if (getColumnCount() > 0) { 2464 anchorCol = 0; 2465 } 2466 anchorSelected = false; 2467 } 2468 2469 // Check the selection here rather than in each selection model. 2470 // This is significant in cell selection mode if we are supposed 2471 // to be toggling the selection. In this case it is better to 2472 // ensure that the cell's selection state will indeed be changed. 2473 // If this were done in the code for the selection model it 2474 // might leave a cell in selection state if the row was 2475 // selected but the column was not - as it would toggle them both. 2476 boolean selected = isCellSelected(rowIndex, columnIndex); 2477 anchorSelected = anchorSelected && isCellSelected(anchorRow, anchorCol); 2478 2479 changeSelectionModel(csm, columnIndex, toggle, extend, selected, 2480 anchorCol, anchorSelected); 2481 changeSelectionModel(rsm, rowIndex, toggle, extend, selected, 2482 anchorRow, anchorSelected); 2483 2484 // Scroll after changing the selection as blit scrolling is immediate, 2485 // so that if we cause the repaint after the scroll we end up painting 2486 // everything! 2487 if (getAutoscrolls()) { 2488 Rectangle cellRect = getCellRect(rowIndex, columnIndex, false); 2489 if (cellRect != null) { 2490 scrollRectToVisible(cellRect); 2491 } 2492 } 2493 } 2494 2495 /** 2496 * Returns the foreground color for selected cells. 2497 * 2498 * @return the <code>Color</code> object for the foreground property 2499 * @see #setSelectionForeground 2500 * @see #setSelectionBackground 2501 */ 2502 public Color getSelectionForeground() { 2503 return selectionForeground; 2504 } 2505 2506 /** 2507 * Sets the foreground color for selected cells. Cell renderers 2508 * can use this color to render text and graphics for selected 2509 * cells. 2510 * <p> 2511 * The default value of this property is defined by the look 2512 * and feel implementation. 2513 * <p> 2514 * This is a <a href="http://docs.oracle.com/javase/tutorial/javabeans/writing/properties.html">JavaBeans</a> bound property. 2515 * 2516 * @param selectionForeground the <code>Color</code> to use in the foreground 2517 * for selected list items 2518 * @see #getSelectionForeground 2519 * @see #setSelectionBackground 2520 * @see #setForeground 2521 * @see #setBackground 2522 * @see #setFont 2523 * @beaninfo 2524 * bound: true 2525 * description: A default foreground color for selected cells. 2526 */ 2527 public void setSelectionForeground(Color selectionForeground) { 2528 Color old = this.selectionForeground; 2529 this.selectionForeground = selectionForeground; 2530 firePropertyChange("selectionForeground", old, selectionForeground); 2531 repaint(); 2532 } 2533 2534 /** 2535 * Returns the background color for selected cells. 2536 * 2537 * @return the <code>Color</code> used for the background of selected list items 2538 * @see #setSelectionBackground 2539 * @see #setSelectionForeground 2540 */ 2541 public Color getSelectionBackground() { 2542 return selectionBackground; 2543 } 2544 2545 /** 2546 * Sets the background color for selected cells. Cell renderers 2547 * can use this color to the fill selected cells. 2548 * <p> 2549 * The default value of this property is defined by the look 2550 * and feel implementation. 2551 * <p> 2552 * This is a <a href="http://docs.oracle.com/javase/tutorial/javabeans/writing/properties.html">JavaBeans</a> bound property. 2553 * 2554 * @param selectionBackground the <code>Color</code> to use for the background 2555 * of selected cells 2556 * @see #getSelectionBackground 2557 * @see #setSelectionForeground 2558 * @see #setForeground 2559 * @see #setBackground 2560 * @see #setFont 2561 * @beaninfo 2562 * bound: true 2563 * description: A default background color for selected cells. 2564 */ 2565 public void setSelectionBackground(Color selectionBackground) { 2566 Color old = this.selectionBackground; 2567 this.selectionBackground = selectionBackground; 2568 firePropertyChange("selectionBackground", old, selectionBackground); 2569 repaint(); 2570 } 2571 2572 /** 2573 * Returns the <code>TableColumn</code> object for the column in the table 2574 * whose identifier is equal to <code>identifier</code>, when compared using 2575 * <code>equals</code>. 2576 * 2577 * @return the <code>TableColumn</code> object that matches the identifier 2578 * @exception IllegalArgumentException if <code>identifier</code> is <code>null</code> or no <code>TableColumn</code> has this identifier 2579 * 2580 * @param identifier the identifier object 2581 */ 2582 public TableColumn getColumn(Object identifier) { 2583 TableColumnModel cm = getColumnModel(); 2584 int columnIndex = cm.getColumnIndex(identifier); 2585 return cm.getColumn(columnIndex); 2586 } 2587 2588 // 2589 // Informally implement the TableModel interface. 2590 // 2591 2592 /** 2593 * Maps the index of the column in the view at 2594 * <code>viewColumnIndex</code> to the index of the column 2595 * in the table model. Returns the index of the corresponding 2596 * column in the model. If <code>viewColumnIndex</code> 2597 * is less than zero, returns <code>viewColumnIndex</code>. 2598 * 2599 * @param viewColumnIndex the index of the column in the view 2600 * @return the index of the corresponding column in the model 2601 * 2602 * @see #convertColumnIndexToView 2603 */ 2604 public int convertColumnIndexToModel(int viewColumnIndex) { 2605 return SwingUtilities2.convertColumnIndexToModel( 2606 getColumnModel(), viewColumnIndex); 2607 } 2608 2609 /** 2610 * Maps the index of the column in the table model at 2611 * <code>modelColumnIndex</code> to the index of the column 2612 * in the view. Returns the index of the 2613 * corresponding column in the view; returns -1 if this column is not 2614 * being displayed. If <code>modelColumnIndex</code> is less than zero, 2615 * returns <code>modelColumnIndex</code>. 2616 * 2617 * @param modelColumnIndex the index of the column in the model 2618 * @return the index of the corresponding column in the view 2619 * 2620 * @see #convertColumnIndexToModel 2621 */ 2622 public int convertColumnIndexToView(int modelColumnIndex) { 2623 return SwingUtilities2.convertColumnIndexToView( 2624 getColumnModel(), modelColumnIndex); 2625 } 2626 2627 /** 2628 * Maps the index of the row in terms of the 2629 * <code>TableModel</code> to the view. If the contents of the 2630 * model are not sorted the model and view indices are the same. 2631 * 2632 * @param modelRowIndex the index of the row in terms of the model 2633 * @return the index of the corresponding row in the view, or -1 if 2634 * the row isn't visible 2635 * @throws IndexOutOfBoundsException if sorting is enabled and passed an 2636 * index outside the number of rows of the <code>TableModel</code> 2637 * @see javax.swing.table.TableRowSorter 2638 * @since 1.6 2639 */ 2640 public int convertRowIndexToView(int modelRowIndex) { 2641 RowSorter<?> sorter = getRowSorter(); 2642 if (sorter != null) { 2643 return sorter.convertRowIndexToView(modelRowIndex); 2644 } 2645 return modelRowIndex; 2646 } 2647 2648 /** 2649 * Maps the index of the row in terms of the view to the 2650 * underlying <code>TableModel</code>. If the contents of the 2651 * model are not sorted the model and view indices are the same. 2652 * 2653 * @param viewRowIndex the index of the row in the view 2654 * @return the index of the corresponding row in the model 2655 * @throws IndexOutOfBoundsException if sorting is enabled and passed an 2656 * index outside the range of the <code>JTable</code> as 2657 * determined by the method <code>getRowCount</code> 2658 * @see javax.swing.table.TableRowSorter 2659 * @see #getRowCount 2660 * @since 1.6 2661 */ 2662 public int convertRowIndexToModel(int viewRowIndex) { 2663 RowSorter<?> sorter = getRowSorter(); 2664 if (sorter != null) { 2665 return sorter.convertRowIndexToModel(viewRowIndex); 2666 } 2667 return viewRowIndex; 2668 } 2669 2670 /** 2671 * Returns the number of rows that can be shown in the 2672 * <code>JTable</code>, given unlimited space. If a 2673 * <code>RowSorter</code> with a filter has been specified, the 2674 * number of rows returned may differ from that of the underlying 2675 * <code>TableModel</code>. 2676 * 2677 * @return the number of rows shown in the <code>JTable</code> 2678 * @see #getColumnCount 2679 */ 2680 public int getRowCount() { 2681 RowSorter<?> sorter = getRowSorter(); 2682 if (sorter != null) { 2683 return sorter.getViewRowCount(); 2684 } 2685 return getModel().getRowCount(); 2686 } 2687 2688 /** 2689 * Returns the number of columns in the column model. Note that this may 2690 * be different from the number of columns in the table model. 2691 * 2692 * @return the number of columns in the table 2693 * @see #getRowCount 2694 * @see #removeColumn 2695 */ 2696 public int getColumnCount() { 2697 return getColumnModel().getColumnCount(); 2698 } 2699 2700 /** 2701 * Returns the name of the column appearing in the view at 2702 * column position <code>column</code>. 2703 * 2704 * @param column the column in the view being queried 2705 * @return the name of the column at position <code>column</code> 2706 in the view where the first column is column 0 2707 */ 2708 public String getColumnName(int column) { 2709 return getModel().getColumnName(convertColumnIndexToModel(column)); 2710 } 2711 2712 /** 2713 * Returns the type of the column appearing in the view at 2714 * column position <code>column</code>. 2715 * 2716 * @param column the column in the view being queried 2717 * @return the type of the column at position <code>column</code> 2718 * in the view where the first column is column 0 2719 */ 2720 public Class<?> getColumnClass(int column) { 2721 return getModel().getColumnClass(convertColumnIndexToModel(column)); 2722 } 2723 2724 /** 2725 * Returns the cell value at <code>row</code> and <code>column</code>. 2726 * <p> 2727 * <b>Note</b>: The column is specified in the table view's display 2728 * order, and not in the <code>TableModel</code>'s column 2729 * order. This is an important distinction because as the 2730 * user rearranges the columns in the table, 2731 * the column at a given index in the view will change. 2732 * Meanwhile the user's actions never affect the model's 2733 * column ordering. 2734 * 2735 * @param row the row whose value is to be queried 2736 * @param column the column whose value is to be queried 2737 * @return the Object at the specified cell 2738 */ 2739 public Object getValueAt(int row, int column) { 2740 return getModel().getValueAt(convertRowIndexToModel(row), 2741 convertColumnIndexToModel(column)); 2742 } 2743 2744 /** 2745 * Sets the value for the cell in the table model at <code>row</code> 2746 * and <code>column</code>. 2747 * <p> 2748 * <b>Note</b>: The column is specified in the table view's display 2749 * order, and not in the <code>TableModel</code>'s column 2750 * order. This is an important distinction because as the 2751 * user rearranges the columns in the table, 2752 * the column at a given index in the view will change. 2753 * Meanwhile the user's actions never affect the model's 2754 * column ordering. 2755 * 2756 * <code>aValue</code> is the new value. 2757 * 2758 * @param aValue the new value 2759 * @param row the row of the cell to be changed 2760 * @param column the column of the cell to be changed 2761 * @see #getValueAt 2762 */ 2763 public void setValueAt(Object aValue, int row, int column) { 2764 getModel().setValueAt(aValue, convertRowIndexToModel(row), 2765 convertColumnIndexToModel(column)); 2766 } 2767 2768 /** 2769 * Returns true if the cell at <code>row</code> and <code>column</code> 2770 * is editable. Otherwise, invoking <code>setValueAt</code> on the cell 2771 * will have no effect. 2772 * <p> 2773 * <b>Note</b>: The column is specified in the table view's display 2774 * order, and not in the <code>TableModel</code>'s column 2775 * order. This is an important distinction because as the 2776 * user rearranges the columns in the table, 2777 * the column at a given index in the view will change. 2778 * Meanwhile the user's actions never affect the model's 2779 * column ordering. 2780 * 2781 * 2782 * @param row the row whose value is to be queried 2783 * @param column the column whose value is to be queried 2784 * @return true if the cell is editable 2785 * @see #setValueAt 2786 */ 2787 public boolean isCellEditable(int row, int column) { 2788 return getModel().isCellEditable(convertRowIndexToModel(row), 2789 convertColumnIndexToModel(column)); 2790 } 2791 // 2792 // Adding and removing columns in the view 2793 // 2794 2795 /** 2796 * Appends <code>aColumn</code> to the end of the array of columns held by 2797 * this <code>JTable</code>'s column model. 2798 * If the column name of <code>aColumn</code> is <code>null</code>, 2799 * sets the column name of <code>aColumn</code> to the name 2800 * returned by <code>getModel().getColumnName()</code>. 2801 * <p> 2802 * To add a column to this <code>JTable</code> to display the 2803 * <code>modelColumn</code>'th column of data in the model with a 2804 * given <code>width</code>, <code>cellRenderer</code>, 2805 * and <code>cellEditor</code> you can use: 2806 * <pre> 2807 * 2808 * addColumn(new TableColumn(modelColumn, width, cellRenderer, cellEditor)); 2809 * 2810 * </pre> 2811 * [Any of the <code>TableColumn</code> constructors can be used 2812 * instead of this one.] 2813 * The model column number is stored inside the <code>TableColumn</code> 2814 * and is used during rendering and editing to locate the appropriates 2815 * data values in the model. The model column number does not change 2816 * when columns are reordered in the view. 2817 * 2818 * @param aColumn the <code>TableColumn</code> to be added 2819 * @see #removeColumn 2820 */ 2821 public void addColumn(TableColumn aColumn) { 2822 if (aColumn.getHeaderValue() == null) { 2823 int modelColumn = aColumn.getModelIndex(); 2824 String columnName = getModel().getColumnName(modelColumn); 2825 aColumn.setHeaderValue(columnName); 2826 } 2827 getColumnModel().addColumn(aColumn); 2828 } 2829 2830 /** 2831 * Removes <code>aColumn</code> from this <code>JTable</code>'s 2832 * array of columns. Note: this method does not remove the column 2833 * of data from the model; it just removes the <code>TableColumn</code> 2834 * that was responsible for displaying it. 2835 * 2836 * @param aColumn the <code>TableColumn</code> to be removed 2837 * @see #addColumn 2838 */ 2839 public void removeColumn(TableColumn aColumn) { 2840 getColumnModel().removeColumn(aColumn); 2841 } 2842 2843 /** 2844 * Moves the column <code>column</code> to the position currently 2845 * occupied by the column <code>targetColumn</code> in the view. 2846 * The old column at <code>targetColumn</code> is 2847 * shifted left or right to make room. 2848 * 2849 * @param column the index of column to be moved 2850 * @param targetColumn the new index of the column 2851 */ 2852 public void moveColumn(int column, int targetColumn) { 2853 getColumnModel().moveColumn(column, targetColumn); 2854 } 2855 2856 // 2857 // Cover methods for various models and helper methods 2858 // 2859 2860 /** 2861 * Returns the index of the column that <code>point</code> lies in, 2862 * or -1 if the result is not in the range 2863 * [0, <code>getColumnCount()</code>-1]. 2864 * 2865 * @param point the location of interest 2866 * @return the index of the column that <code>point</code> lies in, 2867 * or -1 if the result is not in the range 2868 * [0, <code>getColumnCount()</code>-1] 2869 * @see #rowAtPoint 2870 */ 2871 public int columnAtPoint(Point point) { 2872 int x = point.x; 2873 if( !getComponentOrientation().isLeftToRight() ) { 2874 x = getWidth() - x - 1; 2875 } 2876 return getColumnModel().getColumnIndexAtX(x); 2877 } 2878 2879 /** 2880 * Returns the index of the row that <code>point</code> lies in, 2881 * or -1 if the result is not in the range 2882 * [0, <code>getRowCount()</code>-1]. 2883 * 2884 * @param point the location of interest 2885 * @return the index of the row that <code>point</code> lies in, 2886 * or -1 if the result is not in the range 2887 * [0, <code>getRowCount()</code>-1] 2888 * @see #columnAtPoint 2889 */ 2890 public int rowAtPoint(Point point) { 2891 int y = point.y; 2892 int result = (rowModel == null) ? y/getRowHeight() : rowModel.getIndex(y); 2893 if (result < 0) { 2894 return -1; 2895 } 2896 else if (result >= getRowCount()) { 2897 return -1; 2898 } 2899 else { 2900 return result; 2901 } 2902 } 2903 2904 /** 2905 * Returns a rectangle for the cell that lies at the intersection of 2906 * <code>row</code> and <code>column</code>. 2907 * If <code>includeSpacing</code> is true then the value returned 2908 * has the full height and width of the row and column 2909 * specified. If it is false, the returned rectangle is inset by the 2910 * intercell spacing to return the true bounds of the rendering or 2911 * editing component as it will be set during rendering. 2912 * <p> 2913 * If the column index is valid but the row index is less 2914 * than zero the method returns a rectangle with the 2915 * <code>y</code> and <code>height</code> values set appropriately 2916 * and the <code>x</code> and <code>width</code> values both set 2917 * to zero. In general, when either the row or column indices indicate a 2918 * cell outside the appropriate range, the method returns a rectangle 2919 * depicting the closest edge of the closest cell that is within 2920 * the table's range. When both row and column indices are out 2921 * of range the returned rectangle covers the closest 2922 * point of the closest cell. 2923 * <p> 2924 * In all cases, calculations that use this method to calculate 2925 * results along one axis will not fail because of anomalies in 2926 * calculations along the other axis. When the cell is not valid 2927 * the <code>includeSpacing</code> parameter is ignored. 2928 * 2929 * @param row the row index where the desired cell 2930 * is located 2931 * @param column the column index where the desired cell 2932 * is located in the display; this is not 2933 * necessarily the same as the column index 2934 * in the data model for the table; the 2935 * {@link #convertColumnIndexToView(int)} 2936 * method may be used to convert a data 2937 * model column index to a display 2938 * column index 2939 * @param includeSpacing if false, return the true cell bounds - 2940 * computed by subtracting the intercell 2941 * spacing from the height and widths of 2942 * the column and row models 2943 * 2944 * @return the rectangle containing the cell at location 2945 * <code>row</code>,<code>column</code> 2946 * @see #getIntercellSpacing 2947 */ 2948 public Rectangle getCellRect(int row, int column, boolean includeSpacing) { 2949 Rectangle r = new Rectangle(); 2950 boolean valid = true; 2951 if (row < 0) { 2952 // y = height = 0; 2953 valid = false; 2954 } 2955 else if (row >= getRowCount()) { 2956 r.y = getHeight(); 2957 valid = false; 2958 } 2959 else { 2960 r.height = getRowHeight(row); 2961 r.y = (rowModel == null) ? row * r.height : rowModel.getPosition(row); 2962 } 2963 2964 if (column < 0) { 2965 if( !getComponentOrientation().isLeftToRight() ) { 2966 r.x = getWidth(); 2967 } 2968 // otherwise, x = width = 0; 2969 valid = false; 2970 } 2971 else if (column >= getColumnCount()) { 2972 if( getComponentOrientation().isLeftToRight() ) { 2973 r.x = getWidth(); 2974 } 2975 // otherwise, x = width = 0; 2976 valid = false; 2977 } 2978 else { 2979 TableColumnModel cm = getColumnModel(); 2980 if( getComponentOrientation().isLeftToRight() ) { 2981 for(int i = 0; i < column; i++) { 2982 r.x += cm.getColumn(i).getWidth(); 2983 } 2984 } else { 2985 for(int i = cm.getColumnCount()-1; i > column; i--) { 2986 r.x += cm.getColumn(i).getWidth(); 2987 } 2988 } 2989 r.width = cm.getColumn(column).getWidth(); 2990 } 2991 2992 if (valid && !includeSpacing) { 2993 // Bound the margins by their associated dimensions to prevent 2994 // returning bounds with negative dimensions. 2995 int rm = Math.min(getRowMargin(), r.height); 2996 int cm = Math.min(getColumnModel().getColumnMargin(), r.width); 2997 // This is not the same as grow(), it rounds differently. 2998 r.setBounds(r.x + cm/2, r.y + rm/2, r.width - cm, r.height - rm); 2999 } 3000 return r; 3001 } 3002 3003 private int viewIndexForColumn(TableColumn aColumn) { 3004 TableColumnModel cm = getColumnModel(); 3005 for (int column = 0; column < cm.getColumnCount(); column++) { 3006 if (cm.getColumn(column) == aColumn) { 3007 return column; 3008 } 3009 } 3010 return -1; 3011 } 3012 3013 /** 3014 * Causes this table to lay out its rows and columns. Overridden so 3015 * that columns can be resized to accommodate a change in the size of 3016 * a containing parent. 3017 * Resizes one or more of the columns in the table 3018 * so that the total width of all of this <code>JTable</code>'s 3019 * columns is equal to the width of the table. 3020 * <p> 3021 * Before the layout begins the method gets the 3022 * <code>resizingColumn</code> of the <code>tableHeader</code>. 3023 * When the method is called as a result of the resizing of an enclosing window, 3024 * the <code>resizingColumn</code> is <code>null</code>. This means that resizing 3025 * has taken place "outside" the <code>JTable</code> and the change - 3026 * or "delta" - should be distributed to all of the columns regardless 3027 * of this <code>JTable</code>'s automatic resize mode. 3028 * <p> 3029 * If the <code>resizingColumn</code> is not <code>null</code>, it is one of 3030 * the columns in the table that has changed size rather than 3031 * the table itself. In this case the auto-resize modes govern 3032 * the way the extra (or deficit) space is distributed 3033 * amongst the available columns. 3034 * <p> 3035 * The modes are: 3036 * <ul> 3037 * <li> AUTO_RESIZE_OFF: Don't automatically adjust the column's 3038 * widths at all. Use a horizontal scrollbar to accommodate the 3039 * columns when their sum exceeds the width of the 3040 * <code>Viewport</code>. If the <code>JTable</code> is not 3041 * enclosed in a <code>JScrollPane</code> this may 3042 * leave parts of the table invisible. 3043 * <li> AUTO_RESIZE_NEXT_COLUMN: Use just the column after the 3044 * resizing column. This results in the "boundary" or divider 3045 * between adjacent cells being independently adjustable. 3046 * <li> AUTO_RESIZE_SUBSEQUENT_COLUMNS: Use all columns after the 3047 * one being adjusted to absorb the changes. This is the 3048 * default behavior. 3049 * <li> AUTO_RESIZE_LAST_COLUMN: Automatically adjust the 3050 * size of the last column only. If the bounds of the last column 3051 * prevent the desired size from being allocated, set the 3052 * width of the last column to the appropriate limit and make 3053 * no further adjustments. 3054 * <li> AUTO_RESIZE_ALL_COLUMNS: Spread the delta amongst all the columns 3055 * in the <code>JTable</code>, including the one that is being 3056 * adjusted. 3057 * </ul> 3058 * <p> 3059 * <b>Note:</b> When a <code>JTable</code> makes adjustments 3060 * to the widths of the columns it respects their minimum and 3061 * maximum values absolutely. It is therefore possible that, 3062 * even after this method is called, the total width of the columns 3063 * is still not equal to the width of the table. When this happens 3064 * the <code>JTable</code> does not put itself 3065 * in AUTO_RESIZE_OFF mode to bring up a scroll bar, or break other 3066 * commitments of its current auto-resize mode -- instead it 3067 * allows its bounds to be set larger (or smaller) than the total of the 3068 * column minimum or maximum, meaning, either that there 3069 * will not be enough room to display all of the columns, or that the 3070 * columns will not fill the <code>JTable</code>'s bounds. 3071 * These respectively, result in the clipping of some columns 3072 * or an area being painted in the <code>JTable</code>'s 3073 * background color during painting. 3074 * <p> 3075 * The mechanism for distributing the delta amongst the available 3076 * columns is provided in a private method in the <code>JTable</code> 3077 * class: 3078 * <pre> 3079 * adjustSizes(long targetSize, final Resizable3 r, boolean inverse) 3080 * </pre> 3081 * an explanation of which is provided in the following section. 3082 * <code>Resizable3</code> is a private 3083 * interface that allows any data structure containing a collection 3084 * of elements with a size, preferred size, maximum size and minimum size 3085 * to have its elements manipulated by the algorithm. 3086 * 3087 * <H3> Distributing the delta </H3> 3088 * 3089 * <H4> Overview </H4> 3090 * <P> 3091 * Call "DELTA" the difference between the target size and the 3092 * sum of the preferred sizes of the elements in r. The individual 3093 * sizes are calculated by taking the original preferred 3094 * sizes and adding a share of the DELTA - that share being based on 3095 * how far each preferred size is from its limiting bound (minimum or 3096 * maximum). 3097 * 3098 * <H4>Definition</H4> 3099 * <P> 3100 * Call the individual constraints min[i], max[i], and pref[i]. 3101 * <p> 3102 * Call their respective sums: MIN, MAX, and PREF. 3103 * <p> 3104 * Each new size will be calculated using: 3105 * 3106 * <pre> 3107 * size[i] = pref[i] + delta[i] 3108 * </pre> 3109 * where each individual delta[i] is calculated according to: 3110 * <p> 3111 * If (DELTA < 0) we are in shrink mode where: 3112 * 3113 * <PRE> 3114 * DELTA 3115 * delta[i] = ------------ * (pref[i] - min[i]) 3116 * (PREF - MIN) 3117 * </PRE> 3118 * If (DELTA > 0) we are in expand mode where: 3119 * 3120 * <PRE> 3121 * DELTA 3122 * delta[i] = ------------ * (max[i] - pref[i]) 3123 * (MAX - PREF) 3124 * </PRE> 3125 * <P> 3126 * The overall effect is that the total size moves that same percentage, 3127 * k, towards the total minimum or maximum and that percentage guarantees 3128 * accommodation of the required space, DELTA. 3129 * 3130 * <H4>Details</H4> 3131 * <P> 3132 * Naive evaluation of the formulae presented here would be subject to 3133 * the aggregated rounding errors caused by doing this operation in finite 3134 * precision (using ints). To deal with this, the multiplying factor above, 3135 * is constantly recalculated and this takes account of the rounding 3136 * errors in the previous iterations. The result is an algorithm that 3137 * produces a set of integers whose values exactly sum to the supplied 3138 * <code>targetSize</code>, and does so by spreading the rounding 3139 * errors evenly over the given elements. 3140 * 3141 * <H4>When the MAX and MIN bounds are hit</H4> 3142 * <P> 3143 * When <code>targetSize</code> is outside the [MIN, MAX] range, 3144 * the algorithm sets all sizes to their appropriate limiting value 3145 * (maximum or minimum). 3146 * 3147 */ 3148 public void doLayout() { 3149 TableColumn resizingColumn = getResizingColumn(); 3150 if (resizingColumn == null) { 3151 setWidthsFromPreferredWidths(false); 3152 } 3153 else { 3154 // JTable behaves like a layout manger - but one in which the 3155 // user can come along and dictate how big one of the children 3156 // (columns) is supposed to be. 3157 3158 // A column has been resized and JTable may need to distribute 3159 // any overall delta to other columns, according to the resize mode. 3160 int columnIndex = viewIndexForColumn(resizingColumn); 3161 int delta = getWidth() - getColumnModel().getTotalColumnWidth(); 3162 accommodateDelta(columnIndex, delta); 3163 delta = getWidth() - getColumnModel().getTotalColumnWidth(); 3164 3165 // If the delta cannot be completely accomodated, then the 3166 // resizing column will have to take any remainder. This means 3167 // that the column is not being allowed to take the requested 3168 // width. This happens under many circumstances: For example, 3169 // AUTO_RESIZE_NEXT_COLUMN specifies that any delta be distributed 3170 // to the column after the resizing column. If one were to attempt 3171 // to resize the last column of the table, there would be no 3172 // columns after it, and hence nowhere to distribute the delta. 3173 // It would then be given entirely back to the resizing column, 3174 // preventing it from changing size. 3175 if (delta != 0) { 3176 resizingColumn.setWidth(resizingColumn.getWidth() + delta); 3177 } 3178 3179 // At this point the JTable has to work out what preferred sizes 3180 // would have resulted in the layout the user has chosen. 3181 // Thereafter, during window resizing etc. it has to work off 3182 // the preferred sizes as usual - the idea being that, whatever 3183 // the user does, everything stays in synch and things don't jump 3184 // around. 3185 setWidthsFromPreferredWidths(true); 3186 } 3187 3188 super.doLayout(); 3189 } 3190 3191 private TableColumn getResizingColumn() { 3192 return (tableHeader == null) ? null 3193 : tableHeader.getResizingColumn(); 3194 } 3195 3196 /** 3197 * Sizes the table columns to fit the available space. 3198 * 3199 * @param lastColumnOnly determines whether to resize last column only 3200 * @deprecated As of Swing version 1.0.3, 3201 * replaced by <code>doLayout()</code>. 3202 * @see #doLayout 3203 */ 3204 @Deprecated 3205 public void sizeColumnsToFit(boolean lastColumnOnly) { 3206 int oldAutoResizeMode = autoResizeMode; 3207 setAutoResizeMode(lastColumnOnly ? AUTO_RESIZE_LAST_COLUMN 3208 : AUTO_RESIZE_ALL_COLUMNS); 3209 sizeColumnsToFit(-1); 3210 setAutoResizeMode(oldAutoResizeMode); 3211 } 3212 3213 /** 3214 * Obsolete as of Java 2 platform v1.4. Please use the 3215 * <code>doLayout()</code> method instead. 3216 * @param resizingColumn the column whose resizing made this adjustment 3217 * necessary or -1 if there is no such column 3218 * @see #doLayout 3219 */ 3220 public void sizeColumnsToFit(int resizingColumn) { 3221 if (resizingColumn == -1) { 3222 setWidthsFromPreferredWidths(false); 3223 } 3224 else { 3225 if (autoResizeMode == AUTO_RESIZE_OFF) { 3226 TableColumn aColumn = getColumnModel().getColumn(resizingColumn); 3227 aColumn.setPreferredWidth(aColumn.getWidth()); 3228 } 3229 else { 3230 int delta = getWidth() - getColumnModel().getTotalColumnWidth(); 3231 accommodateDelta(resizingColumn, delta); 3232 setWidthsFromPreferredWidths(true); 3233 } 3234 } 3235 } 3236 3237 private void setWidthsFromPreferredWidths(final boolean inverse) { 3238 int totalWidth = getWidth(); 3239 int totalPreferred = getPreferredSize().width; 3240 int target = !inverse ? totalWidth : totalPreferred; 3241 3242 final TableColumnModel cm = columnModel; 3243 Resizable3 r = new Resizable3() { 3244 public int getElementCount() { return cm.getColumnCount(); } 3245 public int getLowerBoundAt(int i) { return cm.getColumn(i).getMinWidth(); } 3246 public int getUpperBoundAt(int i) { return cm.getColumn(i).getMaxWidth(); } 3247 public int getMidPointAt(int i) { 3248 if (!inverse) { 3249 return cm.getColumn(i).getPreferredWidth(); 3250 } 3251 else { 3252 return cm.getColumn(i).getWidth(); 3253 } 3254 } 3255 public void setSizeAt(int s, int i) { 3256 if (!inverse) { 3257 cm.getColumn(i).setWidth(s); 3258 } 3259 else { 3260 cm.getColumn(i).setPreferredWidth(s); 3261 } 3262 } 3263 }; 3264 3265 adjustSizes(target, r, inverse); 3266 } 3267 3268 3269 // Distribute delta over columns, as indicated by the autoresize mode. 3270 private void accommodateDelta(int resizingColumnIndex, int delta) { 3271 int columnCount = getColumnCount(); 3272 int from = resizingColumnIndex; 3273 int to; 3274 3275 // Use the mode to determine how to absorb the changes. 3276 switch(autoResizeMode) { 3277 case AUTO_RESIZE_NEXT_COLUMN: 3278 from = from + 1; 3279 to = Math.min(from + 1, columnCount); break; 3280 case AUTO_RESIZE_SUBSEQUENT_COLUMNS: 3281 from = from + 1; 3282 to = columnCount; break; 3283 case AUTO_RESIZE_LAST_COLUMN: 3284 from = columnCount - 1; 3285 to = from + 1; break; 3286 case AUTO_RESIZE_ALL_COLUMNS: 3287 from = 0; 3288 to = columnCount; break; 3289 default: 3290 return; 3291 } 3292 3293 final int start = from; 3294 final int end = to; 3295 final TableColumnModel cm = columnModel; 3296 Resizable3 r = new Resizable3() { 3297 public int getElementCount() { return end-start; } 3298 public int getLowerBoundAt(int i) { return cm.getColumn(i+start).getMinWidth(); } 3299 public int getUpperBoundAt(int i) { return cm.getColumn(i+start).getMaxWidth(); } 3300 public int getMidPointAt(int i) { return cm.getColumn(i+start).getWidth(); } 3301 public void setSizeAt(int s, int i) { cm.getColumn(i+start).setWidth(s); } 3302 }; 3303 3304 int totalWidth = 0; 3305 for(int i = from; i < to; i++) { 3306 TableColumn aColumn = columnModel.getColumn(i); 3307 int input = aColumn.getWidth(); 3308 totalWidth = totalWidth + input; 3309 } 3310 3311 adjustSizes(totalWidth + delta, r, false); 3312 } 3313 3314 private interface Resizable2 { 3315 public int getElementCount(); 3316 public int getLowerBoundAt(int i); 3317 public int getUpperBoundAt(int i); 3318 public void setSizeAt(int newSize, int i); 3319 } 3320 3321 private interface Resizable3 extends Resizable2 { 3322 public int getMidPointAt(int i); 3323 } 3324 3325 3326 private void adjustSizes(long target, final Resizable3 r, boolean inverse) { 3327 int N = r.getElementCount(); 3328 long totalPreferred = 0; 3329 for(int i = 0; i < N; i++) { 3330 totalPreferred += r.getMidPointAt(i); 3331 } 3332 Resizable2 s; 3333 if ((target < totalPreferred) == !inverse) { 3334 s = new Resizable2() { 3335 public int getElementCount() { return r.getElementCount(); } 3336 public int getLowerBoundAt(int i) { return r.getLowerBoundAt(i); } 3337 public int getUpperBoundAt(int i) { return r.getMidPointAt(i); } 3338 public void setSizeAt(int newSize, int i) { r.setSizeAt(newSize, i); } 3339 3340 }; 3341 } 3342 else { 3343 s = new Resizable2() { 3344 public int getElementCount() { return r.getElementCount(); } 3345 public int getLowerBoundAt(int i) { return r.getMidPointAt(i); } 3346 public int getUpperBoundAt(int i) { return r.getUpperBoundAt(i); } 3347 public void setSizeAt(int newSize, int i) { r.setSizeAt(newSize, i); } 3348 3349 }; 3350 } 3351 adjustSizes(target, s, !inverse); 3352 } 3353 3354 private void adjustSizes(long target, Resizable2 r, boolean limitToRange) { 3355 long totalLowerBound = 0; 3356 long totalUpperBound = 0; 3357 for(int i = 0; i < r.getElementCount(); i++) { 3358 totalLowerBound += r.getLowerBoundAt(i); 3359 totalUpperBound += r.getUpperBoundAt(i); 3360 } 3361 3362 if (limitToRange) { 3363 target = Math.min(Math.max(totalLowerBound, target), totalUpperBound); 3364 } 3365 3366 for(int i = 0; i < r.getElementCount(); i++) { 3367 int lowerBound = r.getLowerBoundAt(i); 3368 int upperBound = r.getUpperBoundAt(i); 3369 // Check for zero. This happens when the distribution of the delta 3370 // finishes early due to a series of "fixed" entries at the end. 3371 // In this case, lowerBound == upperBound, for all subsequent terms. 3372 int newSize; 3373 if (totalLowerBound == totalUpperBound) { 3374 newSize = lowerBound; 3375 } 3376 else { 3377 double f = (double)(target - totalLowerBound)/(totalUpperBound - totalLowerBound); 3378 newSize = (int)Math.round(lowerBound+f*(upperBound - lowerBound)); 3379 // We'd need to round manually in an all integer version. 3380 // size[i] = (int)(((totalUpperBound - target) * lowerBound + 3381 // (target - totalLowerBound) * upperBound)/(totalUpperBound-totalLowerBound)); 3382 } 3383 r.setSizeAt(newSize, i); 3384 target -= newSize; 3385 totalLowerBound -= lowerBound; 3386 totalUpperBound -= upperBound; 3387 } 3388 } 3389 3390 /** 3391 * Overrides <code>JComponent</code>'s <code>getToolTipText</code> 3392 * method in order to allow the renderer's tips to be used 3393 * if it has text set. 3394 * <p> 3395 * <b>Note:</b> For <code>JTable</code> to properly display 3396 * tooltips of its renderers 3397 * <code>JTable</code> must be a registered component with the 3398 * <code>ToolTipManager</code>. 3399 * This is done automatically in <code>initializeLocalVars</code>, 3400 * but if at a later point <code>JTable</code> is told 3401 * <code>setToolTipText(null)</code> it will unregister the table 3402 * component, and no tips from renderers will display anymore. 3403 * 3404 * @see JComponent#getToolTipText 3405 */ 3406 public String getToolTipText(MouseEvent event) { 3407 String tip = null; 3408 Point p = event.getPoint(); 3409 3410 // Locate the renderer under the event location 3411 int hitColumnIndex = columnAtPoint(p); 3412 int hitRowIndex = rowAtPoint(p); 3413 3414 if ((hitColumnIndex != -1) && (hitRowIndex != -1)) { 3415 TableCellRenderer renderer = getCellRenderer(hitRowIndex, hitColumnIndex); 3416 Component component = prepareRenderer(renderer, hitRowIndex, hitColumnIndex); 3417 3418 // Now have to see if the component is a JComponent before 3419 // getting the tip 3420 if (component instanceof JComponent) { 3421 // Convert the event to the renderer's coordinate system 3422 Rectangle cellRect = getCellRect(hitRowIndex, hitColumnIndex, false); 3423 p.translate(-cellRect.x, -cellRect.y); 3424 MouseEvent newEvent = new MouseEvent(component, event.getID(), 3425 event.getWhen(), event.getModifiers(), 3426 p.x, p.y, 3427 event.getXOnScreen(), 3428 event.getYOnScreen(), 3429 event.getClickCount(), 3430 event.isPopupTrigger(), 3431 MouseEvent.NOBUTTON); 3432 3433 tip = ((JComponent)component).getToolTipText(newEvent); 3434 } 3435 } 3436 3437 // No tip from the renderer get our own tip 3438 if (tip == null) 3439 tip = getToolTipText(); 3440 3441 return tip; 3442 } 3443 3444 // 3445 // Editing Support 3446 // 3447 3448 /** 3449 * Sets whether editors in this JTable get the keyboard focus 3450 * when an editor is activated as a result of the JTable 3451 * forwarding keyboard events for a cell. 3452 * By default, this property is false, and the JTable 3453 * retains the focus unless the cell is clicked. 3454 * 3455 * @param surrendersFocusOnKeystroke true if the editor should get the focus 3456 * when keystrokes cause the editor to be 3457 * activated 3458 * 3459 * 3460 * @see #getSurrendersFocusOnKeystroke 3461 * @since 1.4 3462 */ 3463 public void setSurrendersFocusOnKeystroke(boolean surrendersFocusOnKeystroke) { 3464 this.surrendersFocusOnKeystroke = surrendersFocusOnKeystroke; 3465 } 3466 3467 /** 3468 * Returns true if the editor should get the focus 3469 * when keystrokes cause the editor to be activated 3470 * 3471 * @return true if the editor should get the focus 3472 * when keystrokes cause the editor to be 3473 * activated 3474 * 3475 * @see #setSurrendersFocusOnKeystroke 3476 * @since 1.4 3477 */ 3478 public boolean getSurrendersFocusOnKeystroke() { 3479 return surrendersFocusOnKeystroke; 3480 } 3481 3482 /** 3483 * Programmatically starts editing the cell at <code>row</code> and 3484 * <code>column</code>, if those indices are in the valid range, and 3485 * the cell at those indices is editable. 3486 * Note that this is a convenience method for 3487 * <code>editCellAt(int, int, null)</code>. 3488 * 3489 * @param row the row to be edited 3490 * @param column the column to be edited 3491 * @return false if for any reason the cell cannot be edited, 3492 * or if the indices are invalid 3493 */ 3494 public boolean editCellAt(int row, int column) { 3495 return editCellAt(row, column, null); 3496 } 3497 3498 /** 3499 * Programmatically starts editing the cell at <code>row</code> and 3500 * <code>column</code>, if those indices are in the valid range, and 3501 * the cell at those indices is editable. 3502 * To prevent the <code>JTable</code> from 3503 * editing a particular table, column or cell value, return false from 3504 * the <code>isCellEditable</code> method in the <code>TableModel</code> 3505 * interface. 3506 * 3507 * @param row the row to be edited 3508 * @param column the column to be edited 3509 * @param e event to pass into <code>shouldSelectCell</code>; 3510 * note that as of Java 2 platform v1.2, the call to 3511 * <code>shouldSelectCell</code> is no longer made 3512 * @return false if for any reason the cell cannot be edited, 3513 * or if the indices are invalid 3514 */ 3515 public boolean editCellAt(int row, int column, EventObject e){ 3516 if (cellEditor != null && !cellEditor.stopCellEditing()) { 3517 return false; 3518 } 3519 3520 if (row < 0 || row >= getRowCount() || 3521 column < 0 || column >= getColumnCount()) { 3522 return false; 3523 } 3524 3525 if (!isCellEditable(row, column)) 3526 return false; 3527 3528 if (editorRemover == null) { 3529 KeyboardFocusManager fm = 3530 KeyboardFocusManager.getCurrentKeyboardFocusManager(); 3531 editorRemover = new CellEditorRemover(fm); 3532 fm.addPropertyChangeListener("permanentFocusOwner", editorRemover); 3533 } 3534 3535 TableCellEditor editor = getCellEditor(row, column); 3536 if (editor != null && editor.isCellEditable(e)) { 3537 editorComp = prepareEditor(editor, row, column); 3538 if (editorComp == null) { 3539 removeEditor(); 3540 return false; 3541 } 3542 editorComp.setBounds(getCellRect(row, column, false)); 3543 add(editorComp); 3544 editorComp.validate(); 3545 editorComp.repaint(); 3546 3547 setCellEditor(editor); 3548 setEditingRow(row); 3549 setEditingColumn(column); 3550 editor.addCellEditorListener(this); 3551 3552 return true; 3553 } 3554 return false; 3555 } 3556 3557 /** 3558 * Returns true if a cell is being edited. 3559 * 3560 * @return true if the table is editing a cell 3561 * @see #editingColumn 3562 * @see #editingRow 3563 */ 3564 public boolean isEditing() { 3565 return cellEditor != null; 3566 } 3567 3568 /** 3569 * Returns the component that is handling the editing session. 3570 * If nothing is being edited, returns null. 3571 * 3572 * @return Component handling editing session 3573 */ 3574 public Component getEditorComponent() { 3575 return editorComp; 3576 } 3577 3578 /** 3579 * Returns the index of the column that contains the cell currently 3580 * being edited. If nothing is being edited, returns -1. 3581 * 3582 * @return the index of the column that contains the cell currently 3583 * being edited; returns -1 if nothing being edited 3584 * @see #editingRow 3585 */ 3586 public int getEditingColumn() { 3587 return editingColumn; 3588 } 3589 3590 /** 3591 * Returns the index of the row that contains the cell currently 3592 * being edited. If nothing is being edited, returns -1. 3593 * 3594 * @return the index of the row that contains the cell currently 3595 * being edited; returns -1 if nothing being edited 3596 * @see #editingColumn 3597 */ 3598 public int getEditingRow() { 3599 return editingRow; 3600 } 3601 3602 // 3603 // Managing TableUI 3604 // 3605 3606 /** 3607 * Returns the L&F object that renders this component. 3608 * 3609 * @return the <code>TableUI</code> object that renders this component 3610 */ 3611 public TableUI getUI() { 3612 return (TableUI)ui; 3613 } 3614 3615 /** 3616 * Sets the L&F object that renders this component and repaints. 3617 * 3618 * @param ui the TableUI L&F object 3619 * @see UIDefaults#getUI 3620 * @beaninfo 3621 * bound: true 3622 * hidden: true 3623 * attribute: visualUpdate true 3624 * description: The UI object that implements the Component's LookAndFeel. 3625 */ 3626 public void setUI(TableUI ui) { 3627 if (this.ui != ui) { 3628 super.setUI(ui); 3629 repaint(); 3630 } 3631 } 3632 3633 /** 3634 * Notification from the <code>UIManager</code> that the L&F has changed. 3635 * Replaces the current UI object with the latest version from the 3636 * <code>UIManager</code>. 3637 * 3638 * @see JComponent#updateUI 3639 */ 3640 public void updateUI() { 3641 // Update the UIs of the cell renderers, cell editors and header renderers. 3642 TableColumnModel cm = getColumnModel(); 3643 for(int column = 0; column < cm.getColumnCount(); column++) { 3644 TableColumn aColumn = cm.getColumn(column); 3645 SwingUtilities.updateRendererOrEditorUI(aColumn.getCellRenderer()); 3646 SwingUtilities.updateRendererOrEditorUI(aColumn.getCellEditor()); 3647 SwingUtilities.updateRendererOrEditorUI(aColumn.getHeaderRenderer()); 3648 } 3649 3650 // Update the UIs of all the default renderers. 3651 Enumeration<?> defaultRenderers = defaultRenderersByColumnClass.elements(); 3652 while (defaultRenderers.hasMoreElements()) { 3653 SwingUtilities.updateRendererOrEditorUI(defaultRenderers.nextElement()); 3654 } 3655 3656 // Update the UIs of all the default editors. 3657 Enumeration<?> defaultEditors = defaultEditorsByColumnClass.elements(); 3658 while (defaultEditors.hasMoreElements()) { 3659 SwingUtilities.updateRendererOrEditorUI(defaultEditors.nextElement()); 3660 } 3661 3662 // Update the UI of the table header 3663 if (tableHeader != null && tableHeader.getParent() == null) { 3664 tableHeader.updateUI(); 3665 } 3666 3667 // Update UI applied to parent ScrollPane 3668 configureEnclosingScrollPaneUI(); 3669 3670 setUI((TableUI)UIManager.getUI(this)); 3671 } 3672 3673 /** 3674 * Returns the suffix used to construct the name of the L&F class used to 3675 * render this component. 3676 * 3677 * @return the string "TableUI" 3678 * @see JComponent#getUIClassID 3679 * @see UIDefaults#getUI 3680 */ 3681 public String getUIClassID() { 3682 return uiClassID; 3683 } 3684 3685 3686 // 3687 // Managing models 3688 // 3689 3690 /** 3691 * Sets the data model for this table to {@code dataModel} and registers 3692 * with it for listener notifications from the new data model. 3693 * 3694 * @param dataModel the new data source for this table 3695 * @throws IllegalArgumentException if {@code dataModel} is {@code null} 3696 * @see #getModel 3697 * @beaninfo 3698 * bound: true 3699 * description: The model that is the source of the data for this view. 3700 */ 3701 public void setModel(final TableModel dataModel) { 3702 if (dataModel == null) { 3703 throw new IllegalArgumentException("Cannot set a null TableModel"); 3704 } 3705 if (this.dataModel != dataModel) { 3706 TableModel old = this.dataModel; 3707 if (old != null) { 3708 old.removeTableModelListener(this); 3709 } 3710 this.dataModel = dataModel; 3711 dataModel.addTableModelListener(this); 3712 3713 tableChanged(new TableModelEvent(dataModel, TableModelEvent.HEADER_ROW)); 3714 3715 firePropertyChange("model", old, dataModel); 3716 3717 if (getAutoCreateRowSorter()) { 3718 setRowSorter(new TableRowSorter<TableModel>(dataModel)); 3719 } 3720 } 3721 } 3722 3723 /** 3724 * Returns the {@code TableModel} that provides the data displayed by this 3725 * {@code JTable}. 3726 * 3727 * @return the {@code TableModel} that provides the data displayed by this 3728 * {@code JTable} 3729 * @see #setModel 3730 */ 3731 public TableModel getModel() { 3732 return dataModel; 3733 } 3734 3735 /** 3736 * Sets the column model for this table to {@code columnModel} and registers 3737 * for listener notifications from the new column model. Also sets the 3738 * column model of the {@code JTableHeader} to {@code columnModel}. 3739 * 3740 * @param columnModel the new data source for this table 3741 * @throws IllegalArgumentException if {@code columnModel} is {@code null} 3742 * @see #getColumnModel 3743 * @beaninfo 3744 * bound: true 3745 * description: The object governing the way columns appear in the view. 3746 */ 3747 public void setColumnModel(final TableColumnModel columnModel) { 3748 if (columnModel == null) { 3749 throw new IllegalArgumentException("Cannot set a null ColumnModel"); 3750 } 3751 TableColumnModel old = this.columnModel; 3752 if (columnModel != old) { 3753 if (old != null) { 3754 old.removeColumnModelListener(this); 3755 } 3756 this.columnModel = columnModel; 3757 columnModel.addColumnModelListener(this); 3758 3759 // Set the column model of the header as well. 3760 if (tableHeader != null) { 3761 tableHeader.setColumnModel(columnModel); 3762 } 3763 3764 firePropertyChange("columnModel", old, columnModel); 3765 resizeAndRepaint(); 3766 } 3767 } 3768 3769 /** 3770 * Returns the {@code TableColumnModel} that contains all column information 3771 * of this table. 3772 * 3773 * @return the object that provides the column state of the table 3774 * @see #setColumnModel 3775 */ 3776 public TableColumnModel getColumnModel() { 3777 return columnModel; 3778 } 3779 3780 /** 3781 * Sets the row selection model for this table to {@code selectionModel} 3782 * and registers for listener notifications from the new selection model. 3783 * 3784 * @param selectionModel the new selection model 3785 * @throws IllegalArgumentException if {@code selectionModel} is 3786 * {@code null} 3787 * @see #getSelectionModel 3788 * @beaninfo 3789 * bound: true 3790 * description: The selection model for rows. 3791 */ 3792 public void setSelectionModel(final ListSelectionModel selectionModel) { 3793 if (selectionModel == null) { 3794 throw new IllegalArgumentException("Cannot set a null SelectionModel"); 3795 } 3796 3797 ListSelectionModel oldModel = this.selectionModel; 3798 3799 if (selectionModel != oldModel) { 3800 if (oldModel != null) { 3801 oldModel.removeListSelectionListener(this); 3802 } 3803 3804 this.selectionModel = selectionModel; 3805 selectionModel.addListSelectionListener(this); 3806 3807 firePropertyChange("selectionModel", oldModel, selectionModel); 3808 repaint(); 3809 } 3810 } 3811 3812 /** 3813 * Returns the {@code ListSelectionModel} that is used to maintain row 3814 * selection state. 3815 * 3816 * @return the object that provides row selection state, {@code null} if row 3817 * selection is not allowed 3818 * @see #setSelectionModel 3819 */ 3820 public ListSelectionModel getSelectionModel() { 3821 return selectionModel; 3822 } 3823 3824 // 3825 // RowSorterListener 3826 // 3827 3828 /** 3829 * <code>RowSorterListener</code> notification that the 3830 * <code>RowSorter</code> has changed in some way. 3831 * 3832 * @param e the <code>RowSorterEvent</code> describing the change 3833 * @throws NullPointerException if <code>e</code> is <code>null</code> 3834 * @since 1.6 3835 */ 3836 public void sorterChanged(RowSorterEvent e) { 3837 if (e.getType() == RowSorterEvent.Type.SORT_ORDER_CHANGED) { 3838 JTableHeader header = getTableHeader(); 3839 if (header != null) { 3840 header.repaint(); 3841 } 3842 } 3843 else if (e.getType() == RowSorterEvent.Type.SORTED) { 3844 sorterChanged = true; 3845 if (!ignoreSortChange) { 3846 sortedTableChanged(e, null); 3847 } 3848 } 3849 } 3850 3851 3852 /** 3853 * SortManager provides support for managing the selection and variable 3854 * row heights when sorting is enabled. This information is encapsulated 3855 * into a class to avoid bulking up JTable. 3856 */ 3857 private final class SortManager { 3858 RowSorter<? extends TableModel> sorter; 3859 3860 // Selection, in terms of the model. This is lazily created 3861 // as needed. 3862 private ListSelectionModel modelSelection; 3863 private int modelLeadIndex; 3864 // Set to true while in the process of changing the selection. 3865 // If this is true the selection change is ignored. 3866 private boolean syncingSelection; 3867 // Temporary cache of selection, in terms of model. This is only used 3868 // if we don't need the full weight of modelSelection. 3869 private int[] lastModelSelection; 3870 3871 // Heights of the rows in terms of the model. 3872 private SizeSequence modelRowSizes; 3873 3874 3875 SortManager(RowSorter<? extends TableModel> sorter) { 3876 this.sorter = sorter; 3877 sorter.addRowSorterListener(JTable.this); 3878 } 3879 3880 /** 3881 * Disposes any resources used by this SortManager. 3882 */ 3883 public void dispose() { 3884 if (sorter != null) { 3885 sorter.removeRowSorterListener(JTable.this); 3886 } 3887 } 3888 3889 /** 3890 * Sets the height for a row at a specified index. 3891 */ 3892 public void setViewRowHeight(int viewIndex, int rowHeight) { 3893 if (modelRowSizes == null) { 3894 modelRowSizes = new SizeSequence(getModel().getRowCount(), 3895 getRowHeight()); 3896 } 3897 modelRowSizes.setSize(convertRowIndexToModel(viewIndex),rowHeight); 3898 } 3899 3900 /** 3901 * Invoked when the underlying model has completely changed. 3902 */ 3903 public void allChanged() { 3904 modelLeadIndex = -1; 3905 modelSelection = null; 3906 modelRowSizes = null; 3907 } 3908 3909 /** 3910 * Invoked when the selection, on the view, has changed. 3911 */ 3912 public void viewSelectionChanged(ListSelectionEvent e) { 3913 if (!syncingSelection && modelSelection != null) { 3914 modelSelection = null; 3915 } 3916 } 3917 3918 /** 3919 * Invoked when either the table model has changed, or the RowSorter 3920 * has changed. This is invoked prior to notifying the sorter of the 3921 * change. 3922 */ 3923 public void prepareForChange(RowSorterEvent sortEvent, 3924 ModelChange change) { 3925 if (getUpdateSelectionOnSort()) { 3926 cacheSelection(sortEvent, change); 3927 } 3928 } 3929 3930 /** 3931 * Updates the internal cache of the selection based on the change. 3932 */ 3933 private void cacheSelection(RowSorterEvent sortEvent, 3934 ModelChange change) { 3935 if (sortEvent != null) { 3936 // sort order changed. If modelSelection is null and filtering 3937 // is enabled we need to cache the selection in terms of the 3938 // underlying model, this will allow us to correctly restore 3939 // the selection even if rows are filtered out. 3940 if (modelSelection == null && 3941 sorter.getViewRowCount() != getModel().getRowCount()) { 3942 modelSelection = new DefaultListSelectionModel(); 3943 ListSelectionModel viewSelection = getSelectionModel(); 3944 int min = viewSelection.getMinSelectionIndex(); 3945 int max = viewSelection.getMaxSelectionIndex(); 3946 int modelIndex; 3947 for (int viewIndex = min; viewIndex <= max; viewIndex++) { 3948 if (viewSelection.isSelectedIndex(viewIndex)) { 3949 modelIndex = convertRowIndexToModel( 3950 sortEvent, viewIndex); 3951 if (modelIndex != -1) { 3952 modelSelection.addSelectionInterval( 3953 modelIndex, modelIndex); 3954 } 3955 } 3956 } 3957 modelIndex = convertRowIndexToModel(sortEvent, 3958 viewSelection.getLeadSelectionIndex()); 3959 SwingUtilities2.setLeadAnchorWithoutSelection( 3960 modelSelection, modelIndex, modelIndex); 3961 } else if (modelSelection == null) { 3962 // Sorting changed, haven't cached selection in terms 3963 // of model and no filtering. Temporarily cache selection. 3964 cacheModelSelection(sortEvent); 3965 } 3966 } else if (change.allRowsChanged) { 3967 // All the rows have changed, chuck any cached selection. 3968 modelSelection = null; 3969 } else if (modelSelection != null) { 3970 // Table changed, reflect changes in cached selection model. 3971 switch(change.type) { 3972 case TableModelEvent.DELETE: 3973 modelSelection.removeIndexInterval(change.startModelIndex, 3974 change.endModelIndex); 3975 break; 3976 case TableModelEvent.INSERT: 3977 modelSelection.insertIndexInterval(change.startModelIndex, 3978 change.length, 3979 true); 3980 break; 3981 default: 3982 break; 3983 } 3984 } else { 3985 // table changed, but haven't cached rows, temporarily 3986 // cache them. 3987 cacheModelSelection(null); 3988 } 3989 } 3990 3991 private void cacheModelSelection(RowSorterEvent sortEvent) { 3992 lastModelSelection = convertSelectionToModel(sortEvent); 3993 modelLeadIndex = convertRowIndexToModel(sortEvent, 3994 selectionModel.getLeadSelectionIndex()); 3995 } 3996 3997 /** 3998 * Inovked when either the table has changed or the sorter has changed 3999 * and after the sorter has been notified. If necessary this will 4000 * reapply the selection and variable row heights. 4001 */ 4002 public void processChange(RowSorterEvent sortEvent, 4003 ModelChange change, 4004 boolean sorterChanged) { 4005 if (change != null) { 4006 if (change.allRowsChanged) { 4007 modelRowSizes = null; 4008 rowModel = null; 4009 } else if (modelRowSizes != null) { 4010 if (change.type == TableModelEvent.INSERT) { 4011 modelRowSizes.insertEntries(change.startModelIndex, 4012 change.endModelIndex - 4013 change.startModelIndex + 1, 4014 getRowHeight()); 4015 } else if (change.type == TableModelEvent.DELETE) { 4016 modelRowSizes.removeEntries(change.startModelIndex, 4017 change.endModelIndex - 4018 change.startModelIndex +1 ); 4019 } 4020 } 4021 } 4022 if (sorterChanged) { 4023 setViewRowHeightsFromModel(); 4024 restoreSelection(change); 4025 } 4026 } 4027 4028 /** 4029 * Resets the variable row heights in terms of the view from 4030 * that of the variable row heights in terms of the model. 4031 */ 4032 private void setViewRowHeightsFromModel() { 4033 if (modelRowSizes != null) { 4034 rowModel.setSizes(getRowCount(), getRowHeight()); 4035 for (int viewIndex = getRowCount() - 1; viewIndex >= 0; 4036 viewIndex--) { 4037 int modelIndex = convertRowIndexToModel(viewIndex); 4038 rowModel.setSize(viewIndex, 4039 modelRowSizes.getSize(modelIndex)); 4040 } 4041 } 4042 } 4043 4044 /** 4045 * Restores the selection from that in terms of the model. 4046 */ 4047 private void restoreSelection(ModelChange change) { 4048 syncingSelection = true; 4049 if (lastModelSelection != null) { 4050 restoreSortingSelection(lastModelSelection, 4051 modelLeadIndex, change); 4052 lastModelSelection = null; 4053 } else if (modelSelection != null) { 4054 ListSelectionModel viewSelection = getSelectionModel(); 4055 viewSelection.setValueIsAdjusting(true); 4056 viewSelection.clearSelection(); 4057 int min = modelSelection.getMinSelectionIndex(); 4058 int max = modelSelection.getMaxSelectionIndex(); 4059 int viewIndex; 4060 for (int modelIndex = min; modelIndex <= max; modelIndex++) { 4061 if (modelSelection.isSelectedIndex(modelIndex)) { 4062 viewIndex = convertRowIndexToView(modelIndex); 4063 if (viewIndex != -1) { 4064 viewSelection.addSelectionInterval(viewIndex, 4065 viewIndex); 4066 } 4067 } 4068 } 4069 // Restore the lead 4070 int viewLeadIndex = modelSelection.getLeadSelectionIndex(); 4071 if (viewLeadIndex != -1 && !modelSelection.isSelectionEmpty()) { 4072 viewLeadIndex = convertRowIndexToView(viewLeadIndex); 4073 } 4074 SwingUtilities2.setLeadAnchorWithoutSelection( 4075 viewSelection, viewLeadIndex, viewLeadIndex); 4076 viewSelection.setValueIsAdjusting(false); 4077 } 4078 syncingSelection = false; 4079 } 4080 } 4081 4082 4083 /** 4084 * ModelChange is used when sorting to restore state, it corresponds 4085 * to data from a TableModelEvent. The values are precalculated as 4086 * they are used extensively. 4087 */ 4088 private final class ModelChange { 4089 // Starting index of the change, in terms of the model 4090 int startModelIndex; 4091 4092 // Ending index of the change, in terms of the model 4093 int endModelIndex; 4094 4095 // Type of change 4096 int type; 4097 4098 // Number of rows in the model 4099 int modelRowCount; 4100 4101 // The event that triggered this. 4102 TableModelEvent event; 4103 4104 // Length of the change (end - start + 1) 4105 int length; 4106 4107 // True if the event indicates all the contents have changed 4108 boolean allRowsChanged; 4109 4110 ModelChange(TableModelEvent e) { 4111 startModelIndex = Math.max(0, e.getFirstRow()); 4112 endModelIndex = e.getLastRow(); 4113 modelRowCount = getModel().getRowCount(); 4114 if (endModelIndex < 0) { 4115 endModelIndex = Math.max(0, modelRowCount - 1); 4116 } 4117 length = endModelIndex - startModelIndex + 1; 4118 type = e.getType(); 4119 event = e; 4120 allRowsChanged = (e.getLastRow() == Integer.MAX_VALUE); 4121 } 4122 } 4123 4124 /** 4125 * Invoked when <code>sorterChanged</code> is invoked, or 4126 * when <code>tableChanged</code> is invoked and sorting is enabled. 4127 */ 4128 private void sortedTableChanged(RowSorterEvent sortedEvent, 4129 TableModelEvent e) { 4130 int editingModelIndex = -1; 4131 ModelChange change = (e != null) ? new ModelChange(e) : null; 4132 4133 if ((change == null || !change.allRowsChanged) && 4134 this.editingRow != -1) { 4135 editingModelIndex = convertRowIndexToModel(sortedEvent, 4136 this.editingRow); 4137 } 4138 4139 sortManager.prepareForChange(sortedEvent, change); 4140 4141 if (e != null) { 4142 if (change.type == TableModelEvent.UPDATE) { 4143 repaintSortedRows(change); 4144 } 4145 notifySorter(change); 4146 if (change.type != TableModelEvent.UPDATE) { 4147 // If the Sorter is unsorted we will not have received 4148 // notification, force treating insert/delete as a change. 4149 sorterChanged = true; 4150 } 4151 } 4152 else { 4153 sorterChanged = true; 4154 } 4155 4156 sortManager.processChange(sortedEvent, change, sorterChanged); 4157 4158 if (sorterChanged) { 4159 // Update the editing row 4160 if (this.editingRow != -1) { 4161 int newIndex = (editingModelIndex == -1) ? -1 : 4162 convertRowIndexToView(editingModelIndex,change); 4163 restoreSortingEditingRow(newIndex); 4164 } 4165 4166 // And handle the appropriate repainting. 4167 if (e == null || change.type != TableModelEvent.UPDATE) { 4168 resizeAndRepaint(); 4169 } 4170 } 4171 4172 // Check if lead/anchor need to be reset. 4173 if (change != null && change.allRowsChanged) { 4174 clearSelectionAndLeadAnchor(); 4175 resizeAndRepaint(); 4176 } 4177 } 4178 4179 /** 4180 * Repaints the sort of sorted rows in response to a TableModelEvent. 4181 */ 4182 private void repaintSortedRows(ModelChange change) { 4183 if (change.startModelIndex > change.endModelIndex || 4184 change.startModelIndex + 10 < change.endModelIndex) { 4185 // Too much has changed, punt 4186 repaint(); 4187 return; 4188 } 4189 int eventColumn = change.event.getColumn(); 4190 int columnViewIndex = eventColumn; 4191 if (columnViewIndex == TableModelEvent.ALL_COLUMNS) { 4192 columnViewIndex = 0; 4193 } 4194 else { 4195 columnViewIndex = convertColumnIndexToView(columnViewIndex); 4196 if (columnViewIndex == -1) { 4197 return; 4198 } 4199 } 4200 int modelIndex = change.startModelIndex; 4201 while (modelIndex <= change.endModelIndex) { 4202 int viewIndex = convertRowIndexToView(modelIndex++); 4203 if (viewIndex != -1) { 4204 Rectangle dirty = getCellRect(viewIndex, columnViewIndex, 4205 false); 4206 int x = dirty.x; 4207 int w = dirty.width; 4208 if (eventColumn == TableModelEvent.ALL_COLUMNS) { 4209 x = 0; 4210 w = getWidth(); 4211 } 4212 repaint(x, dirty.y, w, dirty.height); 4213 } 4214 } 4215 } 4216 4217 /** 4218 * Restores the selection after a model event/sort order changes. 4219 * All coordinates are in terms of the model. 4220 */ 4221 private void restoreSortingSelection(int[] selection, int lead, 4222 ModelChange change) { 4223 // Convert the selection from model to view 4224 for (int i = selection.length - 1; i >= 0; i--) { 4225 selection[i] = convertRowIndexToView(selection[i], change); 4226 } 4227 lead = convertRowIndexToView(lead, change); 4228 4229 // Check for the common case of no change in selection for 1 row 4230 if (selection.length == 0 || 4231 (selection.length == 1 && selection[0] == getSelectedRow())) { 4232 return; 4233 } 4234 4235 // And apply the new selection 4236 selectionModel.setValueIsAdjusting(true); 4237 selectionModel.clearSelection(); 4238 for (int i = selection.length - 1; i >= 0; i--) { 4239 if (selection[i] != -1) { 4240 selectionModel.addSelectionInterval(selection[i], 4241 selection[i]); 4242 } 4243 } 4244 SwingUtilities2.setLeadAnchorWithoutSelection( 4245 selectionModel, lead, lead); 4246 selectionModel.setValueIsAdjusting(false); 4247 } 4248 4249 /** 4250 * Restores the editing row after a model event/sort order change. 4251 * 4252 * @param editingRow new index of the editingRow, in terms of the view 4253 */ 4254 private void restoreSortingEditingRow(int editingRow) { 4255 if (editingRow == -1) { 4256 // Editing row no longer being shown, cancel editing 4257 TableCellEditor editor = getCellEditor(); 4258 if (editor != null) { 4259 // First try and cancel 4260 editor.cancelCellEditing(); 4261 if (getCellEditor() != null) { 4262 // CellEditor didn't cede control, forcefully 4263 // remove it 4264 removeEditor(); 4265 } 4266 } 4267 } 4268 else { 4269 // Repositioning handled in BasicTableUI 4270 this.editingRow = editingRow; 4271 repaint(); 4272 } 4273 } 4274 4275 /** 4276 * Notifies the sorter of a change in the underlying model. 4277 */ 4278 private void notifySorter(ModelChange change) { 4279 try { 4280 ignoreSortChange = true; 4281 sorterChanged = false; 4282 switch(change.type) { 4283 case TableModelEvent.UPDATE: 4284 if (change.event.getLastRow() == Integer.MAX_VALUE) { 4285 sortManager.sorter.allRowsChanged(); 4286 } else if (change.event.getColumn() == 4287 TableModelEvent.ALL_COLUMNS) { 4288 sortManager.sorter.rowsUpdated(change.startModelIndex, 4289 change.endModelIndex); 4290 } else { 4291 sortManager.sorter.rowsUpdated(change.startModelIndex, 4292 change.endModelIndex, 4293 change.event.getColumn()); 4294 } 4295 break; 4296 case TableModelEvent.INSERT: 4297 sortManager.sorter.rowsInserted(change.startModelIndex, 4298 change.endModelIndex); 4299 break; 4300 case TableModelEvent.DELETE: 4301 sortManager.sorter.rowsDeleted(change.startModelIndex, 4302 change.endModelIndex); 4303 break; 4304 } 4305 } finally { 4306 ignoreSortChange = false; 4307 } 4308 } 4309 4310 /** 4311 * Converts a model index to view index. This is called when the 4312 * sorter or model changes and sorting is enabled. 4313 * 4314 * @param change describes the TableModelEvent that initiated the change; 4315 * will be null if called as the result of a sort 4316 */ 4317 private int convertRowIndexToView(int modelIndex, ModelChange change) { 4318 if (modelIndex < 0) { 4319 return -1; 4320 } 4321 if (change != null && modelIndex >= change.startModelIndex) { 4322 if (change.type == TableModelEvent.INSERT) { 4323 if (modelIndex + change.length >= change.modelRowCount) { 4324 return -1; 4325 } 4326 return sortManager.sorter.convertRowIndexToView( 4327 modelIndex + change.length); 4328 } 4329 else if (change.type == TableModelEvent.DELETE) { 4330 if (modelIndex <= change.endModelIndex) { 4331 // deleted 4332 return -1; 4333 } 4334 else { 4335 if (modelIndex - change.length >= change.modelRowCount) { 4336 return -1; 4337 } 4338 return sortManager.sorter.convertRowIndexToView( 4339 modelIndex - change.length); 4340 } 4341 } 4342 // else, updated 4343 } 4344 if (modelIndex >= getModel().getRowCount()) { 4345 return -1; 4346 } 4347 return sortManager.sorter.convertRowIndexToView(modelIndex); 4348 } 4349 4350 /** 4351 * Converts the selection to model coordinates. This is used when 4352 * the model changes or the sorter changes. 4353 */ 4354 private int[] convertSelectionToModel(RowSorterEvent e) { 4355 int[] selection = getSelectedRows(); 4356 for (int i = selection.length - 1; i >= 0; i--) { 4357 selection[i] = convertRowIndexToModel(e, selection[i]); 4358 } 4359 return selection; 4360 } 4361 4362 private int convertRowIndexToModel(RowSorterEvent e, int viewIndex) { 4363 if (e != null) { 4364 if (e.getPreviousRowCount() == 0) { 4365 return viewIndex; 4366 } 4367 // range checking handled by RowSorterEvent 4368 return e.convertPreviousRowIndexToModel(viewIndex); 4369 } 4370 // Make sure the viewIndex is valid 4371 if (viewIndex < 0 || viewIndex >= getRowCount()) { 4372 return -1; 4373 } 4374 return convertRowIndexToModel(viewIndex); 4375 } 4376 4377 // 4378 // Implementing TableModelListener interface 4379 // 4380 4381 /** 4382 * Invoked when this table's <code>TableModel</code> generates 4383 * a <code>TableModelEvent</code>. 4384 * The <code>TableModelEvent</code> should be constructed in the 4385 * coordinate system of the model; the appropriate mapping to the 4386 * view coordinate system is performed by this <code>JTable</code> 4387 * when it receives the event. 4388 * <p> 4389 * Application code will not use these methods explicitly, they 4390 * are used internally by <code>JTable</code>. 4391 * <p> 4392 * Note that as of 1.3, this method clears the selection, if any. 4393 */ 4394 public void tableChanged(TableModelEvent e) { 4395 if (e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW) { 4396 // The whole thing changed 4397 clearSelectionAndLeadAnchor(); 4398 4399 rowModel = null; 4400 4401 if (sortManager != null) { 4402 try { 4403 ignoreSortChange = true; 4404 sortManager.sorter.modelStructureChanged(); 4405 } finally { 4406 ignoreSortChange = false; 4407 } 4408 sortManager.allChanged(); 4409 } 4410 4411 if (getAutoCreateColumnsFromModel()) { 4412 // This will effect invalidation of the JTable and JTableHeader. 4413 createDefaultColumnsFromModel(); 4414 return; 4415 } 4416 4417 resizeAndRepaint(); 4418 return; 4419 } 4420 4421 if (sortManager != null) { 4422 sortedTableChanged(null, e); 4423 return; 4424 } 4425 4426 // The totalRowHeight calculated below will be incorrect if 4427 // there are variable height rows. Repaint the visible region, 4428 // but don't return as a revalidate may be necessary as well. 4429 if (rowModel != null) { 4430 repaint(); 4431 } 4432 4433 if (e.getType() == TableModelEvent.INSERT) { 4434 tableRowsInserted(e); 4435 return; 4436 } 4437 4438 if (e.getType() == TableModelEvent.DELETE) { 4439 tableRowsDeleted(e); 4440 return; 4441 } 4442 4443 int modelColumn = e.getColumn(); 4444 int start = e.getFirstRow(); 4445 int end = e.getLastRow(); 4446 4447 Rectangle dirtyRegion; 4448 if (modelColumn == TableModelEvent.ALL_COLUMNS) { 4449 // 1 or more rows changed 4450 dirtyRegion = new Rectangle(0, start * getRowHeight(), 4451 getColumnModel().getTotalColumnWidth(), 0); 4452 } 4453 else { 4454 // A cell or column of cells has changed. 4455 // Unlike the rest of the methods in the JTable, the TableModelEvent 4456 // uses the coordinate system of the model instead of the view. 4457 // This is the only place in the JTable where this "reverse mapping" 4458 // is used. 4459 int column = convertColumnIndexToView(modelColumn); 4460 dirtyRegion = getCellRect(start, column, false); 4461 } 4462 4463 // Now adjust the height of the dirty region according to the value of "end". 4464 // Check for Integer.MAX_VALUE as this will cause an overflow. 4465 if (end != Integer.MAX_VALUE) { 4466 dirtyRegion.height = (end-start+1)*getRowHeight(); 4467 repaint(dirtyRegion.x, dirtyRegion.y, dirtyRegion.width, dirtyRegion.height); 4468 } 4469 // In fact, if the end is Integer.MAX_VALUE we need to revalidate anyway 4470 // because the scrollbar may need repainting. 4471 else { 4472 clearSelectionAndLeadAnchor(); 4473 resizeAndRepaint(); 4474 rowModel = null; 4475 } 4476 } 4477 4478 /* 4479 * Invoked when rows have been inserted into the table. 4480 * <p> 4481 * Application code will not use these methods explicitly, they 4482 * are used internally by JTable. 4483 * 4484 * @param e the TableModelEvent encapsulating the insertion 4485 */ 4486 private void tableRowsInserted(TableModelEvent e) { 4487 int start = e.getFirstRow(); 4488 int end = e.getLastRow(); 4489 if (start < 0) { 4490 start = 0; 4491 } 4492 if (end < 0) { 4493 end = getRowCount()-1; 4494 } 4495 4496 // Adjust the selection to account for the new rows. 4497 int length = end - start + 1; 4498 selectionModel.insertIndexInterval(start, length, true); 4499 4500 // If we have variable height rows, adjust the row model. 4501 if (rowModel != null) { 4502 rowModel.insertEntries(start, length, getRowHeight()); 4503 } 4504 int rh = getRowHeight() ; 4505 Rectangle drawRect = new Rectangle(0, start * rh, 4506 getColumnModel().getTotalColumnWidth(), 4507 (getRowCount()-start) * rh); 4508 4509 revalidate(); 4510 // PENDING(milne) revalidate calls repaint() if parent is a ScrollPane 4511 // repaint still required in the unusual case where there is no ScrollPane 4512 repaint(drawRect); 4513 } 4514 4515 /* 4516 * Invoked when rows have been removed from the table. 4517 * <p> 4518 * Application code will not use these methods explicitly, they 4519 * are used internally by JTable. 4520 * 4521 * @param e the TableModelEvent encapsulating the deletion 4522 */ 4523 private void tableRowsDeleted(TableModelEvent e) { 4524 int start = e.getFirstRow(); 4525 int end = e.getLastRow(); 4526 if (start < 0) { 4527 start = 0; 4528 } 4529 if (end < 0) { 4530 end = getRowCount()-1; 4531 } 4532 4533 int deletedCount = end - start + 1; 4534 int previousRowCount = getRowCount() + deletedCount; 4535 // Adjust the selection to account for the new rows 4536 selectionModel.removeIndexInterval(start, end); 4537 4538 // If we have variable height rows, adjust the row model. 4539 if (rowModel != null) { 4540 rowModel.removeEntries(start, deletedCount); 4541 } 4542 4543 int rh = getRowHeight(); 4544 Rectangle drawRect = new Rectangle(0, start * rh, 4545 getColumnModel().getTotalColumnWidth(), 4546 (previousRowCount - start) * rh); 4547 4548 revalidate(); 4549 // PENDING(milne) revalidate calls repaint() if parent is a ScrollPane 4550 // repaint still required in the unusual case where there is no ScrollPane 4551 repaint(drawRect); 4552 } 4553 4554 // 4555 // Implementing TableColumnModelListener interface 4556 // 4557 4558 /** 4559 * Invoked when a column is added to the table column model. 4560 * <p> 4561 * Application code will not use these methods explicitly, they 4562 * are used internally by JTable. 4563 * 4564 * @see TableColumnModelListener 4565 */ 4566 public void columnAdded(TableColumnModelEvent e) { 4567 // If I'm currently editing, then I should stop editing 4568 if (isEditing()) { 4569 removeEditor(); 4570 } 4571 resizeAndRepaint(); 4572 } 4573 4574 /** 4575 * Invoked when a column is removed from the table column model. 4576 * <p> 4577 * Application code will not use these methods explicitly, they 4578 * are used internally by JTable. 4579 * 4580 * @see TableColumnModelListener 4581 */ 4582 public void columnRemoved(TableColumnModelEvent e) { 4583 // If I'm currently editing, then I should stop editing 4584 if (isEditing()) { 4585 removeEditor(); 4586 } 4587 resizeAndRepaint(); 4588 } 4589 4590 /** 4591 * Invoked when a column is repositioned. If a cell is being 4592 * edited, then editing is stopped and the cell is redrawn. 4593 * <p> 4594 * Application code will not use these methods explicitly, they 4595 * are used internally by JTable. 4596 * 4597 * @param e the event received 4598 * @see TableColumnModelListener 4599 */ 4600 public void columnMoved(TableColumnModelEvent e) { 4601 if (isEditing() && !getCellEditor().stopCellEditing()) { 4602 getCellEditor().cancelCellEditing(); 4603 } 4604 repaint(); 4605 } 4606 4607 /** 4608 * Invoked when a column is moved due to a margin change. 4609 * If a cell is being edited, then editing is stopped and the cell 4610 * is redrawn. 4611 * <p> 4612 * Application code will not use these methods explicitly, they 4613 * are used internally by JTable. 4614 * 4615 * @param e the event received 4616 * @see TableColumnModelListener 4617 */ 4618 public void columnMarginChanged(ChangeEvent e) { 4619 if (isEditing() && !getCellEditor().stopCellEditing()) { 4620 getCellEditor().cancelCellEditing(); 4621 } 4622 TableColumn resizingColumn = getResizingColumn(); 4623 // Need to do this here, before the parent's 4624 // layout manager calls getPreferredSize(). 4625 if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF) { 4626 resizingColumn.setPreferredWidth(resizingColumn.getWidth()); 4627 } 4628 resizeAndRepaint(); 4629 } 4630 4631 private int limit(int i, int a, int b) { 4632 return Math.min(b, Math.max(i, a)); 4633 } 4634 4635 /** 4636 * Invoked when the selection model of the <code>TableColumnModel</code> 4637 * is changed. 4638 * <p> 4639 * Application code will not use these methods explicitly, they 4640 * are used internally by JTable. 4641 * 4642 * @param e the event received 4643 * @see TableColumnModelListener 4644 */ 4645 public void columnSelectionChanged(ListSelectionEvent e) { 4646 boolean isAdjusting = e.getValueIsAdjusting(); 4647 if (columnSelectionAdjusting && !isAdjusting) { 4648 // The assumption is that when the model is no longer adjusting 4649 // we will have already gotten all the changes, and therefore 4650 // don't need to do an additional paint. 4651 columnSelectionAdjusting = false; 4652 return; 4653 } 4654 columnSelectionAdjusting = isAdjusting; 4655 // The getCellRect() call will fail unless there is at least one row. 4656 if (getRowCount() <= 0 || getColumnCount() <= 0) { 4657 return; 4658 } 4659 int firstIndex = limit(e.getFirstIndex(), 0, getColumnCount()-1); 4660 int lastIndex = limit(e.getLastIndex(), 0, getColumnCount()-1); 4661 int minRow = 0; 4662 int maxRow = getRowCount() - 1; 4663 if (getRowSelectionAllowed()) { 4664 minRow = selectionModel.getMinSelectionIndex(); 4665 maxRow = selectionModel.getMaxSelectionIndex(); 4666 int leadRow = getAdjustedIndex(selectionModel.getLeadSelectionIndex(), true); 4667 4668 if (minRow == -1 || maxRow == -1) { 4669 if (leadRow == -1) { 4670 // nothing to repaint, return 4671 return; 4672 } 4673 4674 // only thing to repaint is the lead 4675 minRow = maxRow = leadRow; 4676 } else { 4677 // We need to consider more than just the range between 4678 // the min and max selected index. The lead row, which could 4679 // be outside this range, should be considered also. 4680 if (leadRow != -1) { 4681 minRow = Math.min(minRow, leadRow); 4682 maxRow = Math.max(maxRow, leadRow); 4683 } 4684 } 4685 } 4686 Rectangle firstColumnRect = getCellRect(minRow, firstIndex, false); 4687 Rectangle lastColumnRect = getCellRect(maxRow, lastIndex, false); 4688 Rectangle dirtyRegion = firstColumnRect.union(lastColumnRect); 4689 repaint(dirtyRegion); 4690 } 4691 4692 // 4693 // Implementing ListSelectionListener interface 4694 // 4695 4696 /** 4697 * Invoked when the row selection changes -- repaints to show the new 4698 * selection. 4699 * <p> 4700 * Application code will not use these methods explicitly, they 4701 * are used internally by JTable. 4702 * 4703 * @param e the event received 4704 * @see ListSelectionListener 4705 */ 4706 public void valueChanged(ListSelectionEvent e) { 4707 if (sortManager != null) { 4708 sortManager.viewSelectionChanged(e); 4709 } 4710 boolean isAdjusting = e.getValueIsAdjusting(); 4711 if (rowSelectionAdjusting && !isAdjusting) { 4712 // The assumption is that when the model is no longer adjusting 4713 // we will have already gotten all the changes, and therefore 4714 // don't need to do an additional paint. 4715 rowSelectionAdjusting = false; 4716 return; 4717 } 4718 rowSelectionAdjusting = isAdjusting; 4719 // The getCellRect() calls will fail unless there is at least one column. 4720 if (getRowCount() <= 0 || getColumnCount() <= 0) { 4721 return; 4722 } 4723 int firstIndex = limit(e.getFirstIndex(), 0, getRowCount()-1); 4724 int lastIndex = limit(e.getLastIndex(), 0, getRowCount()-1); 4725 Rectangle firstRowRect = getCellRect(firstIndex, 0, false); 4726 Rectangle lastRowRect = getCellRect(lastIndex, getColumnCount()-1, false); 4727 Rectangle dirtyRegion = firstRowRect.union(lastRowRect); 4728 repaint(dirtyRegion); 4729 } 4730 4731 // 4732 // Implementing the CellEditorListener interface 4733 // 4734 4735 /** 4736 * Invoked when editing is finished. The changes are saved and the 4737 * editor is discarded. 4738 * <p> 4739 * Application code will not use these methods explicitly, they 4740 * are used internally by JTable. 4741 * 4742 * @param e the event received 4743 * @see CellEditorListener 4744 */ 4745 public void editingStopped(ChangeEvent e) { 4746 // Take in the new value 4747 TableCellEditor editor = getCellEditor(); 4748 if (editor != null) { 4749 Object value = editor.getCellEditorValue(); 4750 setValueAt(value, editingRow, editingColumn); 4751 removeEditor(); 4752 } 4753 } 4754 4755 /** 4756 * Invoked when editing is canceled. The editor object is discarded 4757 * and the cell is rendered once again. 4758 * <p> 4759 * Application code will not use these methods explicitly, they 4760 * are used internally by JTable. 4761 * 4762 * @param e the event received 4763 * @see CellEditorListener 4764 */ 4765 public void editingCanceled(ChangeEvent e) { 4766 removeEditor(); 4767 } 4768 4769 // 4770 // Implementing the Scrollable interface 4771 // 4772 4773 /** 4774 * Sets the preferred size of the viewport for this table. 4775 * 4776 * @param size a <code>Dimension</code> object specifying the <code>preferredSize</code> of a 4777 * <code>JViewport</code> whose view is this table 4778 * @see Scrollable#getPreferredScrollableViewportSize 4779 * @beaninfo 4780 * description: The preferred size of the viewport. 4781 */ 4782 public void setPreferredScrollableViewportSize(Dimension size) { 4783 preferredViewportSize = size; 4784 } 4785 4786 /** 4787 * Returns the preferred size of the viewport for this table. 4788 * 4789 * @return a <code>Dimension</code> object containing the <code>preferredSize</code> of the <code>JViewport</code> 4790 * which displays this table 4791 * @see Scrollable#getPreferredScrollableViewportSize 4792 */ 4793 public Dimension getPreferredScrollableViewportSize() { 4794 return preferredViewportSize; 4795 } 4796 4797 /** 4798 * Returns the scroll increment (in pixels) that completely exposes one new 4799 * row or column (depending on the orientation). 4800 * <p> 4801 * This method is called each time the user requests a unit scroll. 4802 * 4803 * @param visibleRect the view area visible within the viewport 4804 * @param orientation either <code>SwingConstants.VERTICAL</code> 4805 * or <code>SwingConstants.HORIZONTAL</code> 4806 * @param direction less than zero to scroll up/left, 4807 * greater than zero for down/right 4808 * @return the "unit" increment for scrolling in the specified direction 4809 * @see Scrollable#getScrollableUnitIncrement 4810 */ 4811 public int getScrollableUnitIncrement(Rectangle visibleRect, 4812 int orientation, 4813 int direction) { 4814 int leadingRow; 4815 int leadingCol; 4816 Rectangle leadingCellRect; 4817 4818 int leadingVisibleEdge; 4819 int leadingCellEdge; 4820 int leadingCellSize; 4821 4822 leadingRow = getLeadingRow(visibleRect); 4823 leadingCol = getLeadingCol(visibleRect); 4824 if (orientation == SwingConstants.VERTICAL && leadingRow < 0) { 4825 // Couldn't find leading row - return some default value 4826 return getRowHeight(); 4827 } 4828 else if (orientation == SwingConstants.HORIZONTAL && leadingCol < 0) { 4829 // Couldn't find leading col - return some default value 4830 return 100; 4831 } 4832 4833 // Note that it's possible for one of leadingCol or leadingRow to be 4834 // -1, depending on the orientation. This is okay, as getCellRect() 4835 // still provides enough information to calculate the unit increment. 4836 leadingCellRect = getCellRect(leadingRow, leadingCol, true); 4837 leadingVisibleEdge = leadingEdge(visibleRect, orientation); 4838 leadingCellEdge = leadingEdge(leadingCellRect, orientation); 4839 4840 if (orientation == SwingConstants.VERTICAL) { 4841 leadingCellSize = leadingCellRect.height; 4842 4843 } 4844 else { 4845 leadingCellSize = leadingCellRect.width; 4846 } 4847 4848 // 4 cases: 4849 // #1: Leading cell fully visible, reveal next cell 4850 // #2: Leading cell fully visible, hide leading cell 4851 // #3: Leading cell partially visible, hide rest of leading cell 4852 // #4: Leading cell partially visible, reveal rest of leading cell 4853 4854 if (leadingVisibleEdge == leadingCellEdge) { // Leading cell is fully 4855 // visible 4856 // Case #1: Reveal previous cell 4857 if (direction < 0) { 4858 int retVal = 0; 4859 4860 if (orientation == SwingConstants.VERTICAL) { 4861 // Loop past any zero-height rows 4862 while (--leadingRow >= 0) { 4863 retVal = getRowHeight(leadingRow); 4864 if (retVal != 0) { 4865 break; 4866 } 4867 } 4868 } 4869 else { // HORIZONTAL 4870 // Loop past any zero-width cols 4871 while (--leadingCol >= 0) { 4872 retVal = getCellRect(leadingRow, leadingCol, true).width; 4873 if (retVal != 0) { 4874 break; 4875 } 4876 } 4877 } 4878 return retVal; 4879 } 4880 else { // Case #2: hide leading cell 4881 return leadingCellSize; 4882 } 4883 } 4884 else { // Leading cell is partially hidden 4885 // Compute visible, hidden portions 4886 int hiddenAmt = Math.abs(leadingVisibleEdge - leadingCellEdge); 4887 int visibleAmt = leadingCellSize - hiddenAmt; 4888 4889 if (direction > 0) { 4890 // Case #3: hide showing portion of leading cell 4891 return visibleAmt; 4892 } 4893 else { // Case #4: reveal hidden portion of leading cell 4894 return hiddenAmt; 4895 } 4896 } 4897 } 4898 4899 /** 4900 * Returns <code>visibleRect.height</code> or 4901 * <code>visibleRect.width</code>, 4902 * depending on this table's orientation. Note that as of Swing 1.1.1 4903 * (Java 2 v 1.2.2) the value 4904 * returned will ensure that the viewport is cleanly aligned on 4905 * a row boundary. 4906 * 4907 * @return <code>visibleRect.height</code> or 4908 * <code>visibleRect.width</code> 4909 * per the orientation 4910 * @see Scrollable#getScrollableBlockIncrement 4911 */ 4912 public int getScrollableBlockIncrement(Rectangle visibleRect, 4913 int orientation, int direction) { 4914 4915 if (getRowCount() == 0) { 4916 // Short-circuit empty table model 4917 if (SwingConstants.VERTICAL == orientation) { 4918 int rh = getRowHeight(); 4919 return (rh > 0) ? Math.max(rh, (visibleRect.height / rh) * rh) : 4920 visibleRect.height; 4921 } 4922 else { 4923 return visibleRect.width; 4924 } 4925 } 4926 // Shortcut for vertical scrolling of a table w/ uniform row height 4927 if (null == rowModel && SwingConstants.VERTICAL == orientation) { 4928 int row = rowAtPoint(visibleRect.getLocation()); 4929 assert row != -1; 4930 int col = columnAtPoint(visibleRect.getLocation()); 4931 Rectangle cellRect = getCellRect(row, col, true); 4932 4933 if (cellRect.y == visibleRect.y) { 4934 int rh = getRowHeight(); 4935 assert rh > 0; 4936 return Math.max(rh, (visibleRect.height / rh) * rh); 4937 } 4938 } 4939 if (direction < 0) { 4940 return getPreviousBlockIncrement(visibleRect, orientation); 4941 } 4942 else { 4943 return getNextBlockIncrement(visibleRect, orientation); 4944 } 4945 } 4946 4947 /** 4948 * Called to get the block increment for upward scrolling in cases of 4949 * horizontal scrolling, or for vertical scrolling of a table with 4950 * variable row heights. 4951 */ 4952 private int getPreviousBlockIncrement(Rectangle visibleRect, 4953 int orientation) { 4954 // Measure back from visible leading edge 4955 // If we hit the cell on its leading edge, it becomes the leading cell. 4956 // Else, use following cell 4957 4958 int row; 4959 int col; 4960 4961 int newEdge; 4962 Point newCellLoc; 4963 4964 int visibleLeadingEdge = leadingEdge(visibleRect, orientation); 4965 boolean leftToRight = getComponentOrientation().isLeftToRight(); 4966 int newLeadingEdge; 4967 4968 // Roughly determine the new leading edge by measuring back from the 4969 // leading visible edge by the size of the visible rect, and find the 4970 // cell there. 4971 if (orientation == SwingConstants.VERTICAL) { 4972 newEdge = visibleLeadingEdge - visibleRect.height; 4973 int x = visibleRect.x + (leftToRight ? 0 : visibleRect.width); 4974 newCellLoc = new Point(x, newEdge); 4975 } 4976 else if (leftToRight) { 4977 newEdge = visibleLeadingEdge - visibleRect.width; 4978 newCellLoc = new Point(newEdge, visibleRect.y); 4979 } 4980 else { // Horizontal, right-to-left 4981 newEdge = visibleLeadingEdge + visibleRect.width; 4982 newCellLoc = new Point(newEdge - 1, visibleRect.y); 4983 } 4984 row = rowAtPoint(newCellLoc); 4985 col = columnAtPoint(newCellLoc); 4986 4987 // If we're measuring past the beginning of the table, we get an invalid 4988 // cell. Just go to the beginning of the table in this case. 4989 if (orientation == SwingConstants.VERTICAL & row < 0) { 4990 newLeadingEdge = 0; 4991 } 4992 else if (orientation == SwingConstants.HORIZONTAL & col < 0) { 4993 if (leftToRight) { 4994 newLeadingEdge = 0; 4995 } 4996 else { 4997 newLeadingEdge = getWidth(); 4998 } 4999 } 5000 else { 5001 // Refine our measurement 5002 Rectangle newCellRect = getCellRect(row, col, true); 5003 int newCellLeadingEdge = leadingEdge(newCellRect, orientation); 5004 int newCellTrailingEdge = trailingEdge(newCellRect, orientation); 5005 5006 // Usually, we hit in the middle of newCell, and want to scroll to 5007 // the beginning of the cell after newCell. But there are a 5008 // couple corner cases where we want to scroll to the beginning of 5009 // newCell itself. These cases are: 5010 // 1) newCell is so large that it ends at or extends into the 5011 // visibleRect (newCell is the leading cell, or is adjacent to 5012 // the leading cell) 5013 // 2) newEdge happens to fall right on the beginning of a cell 5014 5015 // Case 1 5016 if ((orientation == SwingConstants.VERTICAL || leftToRight) && 5017 (newCellTrailingEdge >= visibleLeadingEdge)) { 5018 newLeadingEdge = newCellLeadingEdge; 5019 } 5020 else if (orientation == SwingConstants.HORIZONTAL && 5021 !leftToRight && 5022 newCellTrailingEdge <= visibleLeadingEdge) { 5023 newLeadingEdge = newCellLeadingEdge; 5024 } 5025 // Case 2: 5026 else if (newEdge == newCellLeadingEdge) { 5027 newLeadingEdge = newCellLeadingEdge; 5028 } 5029 // Common case: scroll to cell after newCell 5030 else { 5031 newLeadingEdge = newCellTrailingEdge; 5032 } 5033 } 5034 return Math.abs(visibleLeadingEdge - newLeadingEdge); 5035 } 5036 5037 /** 5038 * Called to get the block increment for downward scrolling in cases of 5039 * horizontal scrolling, or for vertical scrolling of a table with 5040 * variable row heights. 5041 */ 5042 private int getNextBlockIncrement(Rectangle visibleRect, 5043 int orientation) { 5044 // Find the cell at the trailing edge. Return the distance to put 5045 // that cell at the leading edge. 5046 int trailingRow = getTrailingRow(visibleRect); 5047 int trailingCol = getTrailingCol(visibleRect); 5048 5049 Rectangle cellRect; 5050 boolean cellFillsVis; 5051 5052 int cellLeadingEdge; 5053 int cellTrailingEdge; 5054 int newLeadingEdge; 5055 int visibleLeadingEdge = leadingEdge(visibleRect, orientation); 5056 5057 // If we couldn't find trailing cell, just return the size of the 5058 // visibleRect. Note that, for instance, we don't need the 5059 // trailingCol to proceed if we're scrolling vertically, because 5060 // cellRect will still fill in the required dimensions. This would 5061 // happen if we're scrolling vertically, and the table is not wide 5062 // enough to fill the visibleRect. 5063 if (orientation == SwingConstants.VERTICAL && trailingRow < 0) { 5064 return visibleRect.height; 5065 } 5066 else if (orientation == SwingConstants.HORIZONTAL && trailingCol < 0) { 5067 return visibleRect.width; 5068 } 5069 cellRect = getCellRect(trailingRow, trailingCol, true); 5070 cellLeadingEdge = leadingEdge(cellRect, orientation); 5071 cellTrailingEdge = trailingEdge(cellRect, orientation); 5072 5073 if (orientation == SwingConstants.VERTICAL || 5074 getComponentOrientation().isLeftToRight()) { 5075 cellFillsVis = cellLeadingEdge <= visibleLeadingEdge; 5076 } 5077 else { // Horizontal, right-to-left 5078 cellFillsVis = cellLeadingEdge >= visibleLeadingEdge; 5079 } 5080 5081 if (cellFillsVis) { 5082 // The visibleRect contains a single large cell. Scroll to the end 5083 // of this cell, so the following cell is the first cell. 5084 newLeadingEdge = cellTrailingEdge; 5085 } 5086 else if (cellTrailingEdge == trailingEdge(visibleRect, orientation)) { 5087 // The trailing cell happens to end right at the end of the 5088 // visibleRect. Again, scroll to the beginning of the next cell. 5089 newLeadingEdge = cellTrailingEdge; 5090 } 5091 else { 5092 // Common case: the trailing cell is partially visible, and isn't 5093 // big enough to take up the entire visibleRect. Scroll so it 5094 // becomes the leading cell. 5095 newLeadingEdge = cellLeadingEdge; 5096 } 5097 return Math.abs(newLeadingEdge - visibleLeadingEdge); 5098 } 5099 5100 /* 5101 * Return the row at the top of the visibleRect 5102 * 5103 * May return -1 5104 */ 5105 private int getLeadingRow(Rectangle visibleRect) { 5106 Point leadingPoint; 5107 5108 if (getComponentOrientation().isLeftToRight()) { 5109 leadingPoint = new Point(visibleRect.x, visibleRect.y); 5110 } 5111 else { 5112 leadingPoint = new Point(visibleRect.x + visibleRect.width - 1, 5113 visibleRect.y); 5114 } 5115 return rowAtPoint(leadingPoint); 5116 } 5117 5118 /* 5119 * Return the column at the leading edge of the visibleRect. 5120 * 5121 * May return -1 5122 */ 5123 private int getLeadingCol(Rectangle visibleRect) { 5124 Point leadingPoint; 5125 5126 if (getComponentOrientation().isLeftToRight()) { 5127 leadingPoint = new Point(visibleRect.x, visibleRect.y); 5128 } 5129 else { 5130 leadingPoint = new Point(visibleRect.x + visibleRect.width - 1, 5131 visibleRect.y); 5132 } 5133 return columnAtPoint(leadingPoint); 5134 } 5135 5136 /* 5137 * Return the row at the bottom of the visibleRect. 5138 * 5139 * May return -1 5140 */ 5141 private int getTrailingRow(Rectangle visibleRect) { 5142 Point trailingPoint; 5143 5144 if (getComponentOrientation().isLeftToRight()) { 5145 trailingPoint = new Point(visibleRect.x, 5146 visibleRect.y + visibleRect.height - 1); 5147 } 5148 else { 5149 trailingPoint = new Point(visibleRect.x + visibleRect.width - 1, 5150 visibleRect.y + visibleRect.height - 1); 5151 } 5152 return rowAtPoint(trailingPoint); 5153 } 5154 5155 /* 5156 * Return the column at the trailing edge of the visibleRect. 5157 * 5158 * May return -1 5159 */ 5160 private int getTrailingCol(Rectangle visibleRect) { 5161 Point trailingPoint; 5162 5163 if (getComponentOrientation().isLeftToRight()) { 5164 trailingPoint = new Point(visibleRect.x + visibleRect.width - 1, 5165 visibleRect.y); 5166 } 5167 else { 5168 trailingPoint = new Point(visibleRect.x, visibleRect.y); 5169 } 5170 return columnAtPoint(trailingPoint); 5171 } 5172 5173 /* 5174 * Returns the leading edge ("beginning") of the given Rectangle. 5175 * For VERTICAL, this is the top, for left-to-right, the left side, and for 5176 * right-to-left, the right side. 5177 */ 5178 private int leadingEdge(Rectangle rect, int orientation) { 5179 if (orientation == SwingConstants.VERTICAL) { 5180 return rect.y; 5181 } 5182 else if (getComponentOrientation().isLeftToRight()) { 5183 return rect.x; 5184 } 5185 else { // Horizontal, right-to-left 5186 return rect.x + rect.width; 5187 } 5188 } 5189 5190 /* 5191 * Returns the trailing edge ("end") of the given Rectangle. 5192 * For VERTICAL, this is the bottom, for left-to-right, the right side, and 5193 * for right-to-left, the left side. 5194 */ 5195 private int trailingEdge(Rectangle rect, int orientation) { 5196 if (orientation == SwingConstants.VERTICAL) { 5197 return rect.y + rect.height; 5198 } 5199 else if (getComponentOrientation().isLeftToRight()) { 5200 return rect.x + rect.width; 5201 } 5202 else { // Horizontal, right-to-left 5203 return rect.x; 5204 } 5205 } 5206 5207 /** 5208 * Returns false if <code>autoResizeMode</code> is set to 5209 * <code>AUTO_RESIZE_OFF</code>, which indicates that the 5210 * width of the viewport does not determine the width 5211 * of the table. Otherwise returns true. 5212 * 5213 * @return false if <code>autoResizeMode</code> is set 5214 * to <code>AUTO_RESIZE_OFF</code>, otherwise returns true 5215 * @see Scrollable#getScrollableTracksViewportWidth 5216 */ 5217 public boolean getScrollableTracksViewportWidth() { 5218 return !(autoResizeMode == AUTO_RESIZE_OFF); 5219 } 5220 5221 /** 5222 * Returns {@code false} to indicate that the height of the viewport does 5223 * not determine the height of the table, unless 5224 * {@code getFillsViewportHeight} is {@code true} and the preferred height 5225 * of the table is smaller than the viewport's height. 5226 * 5227 * @return {@code false} unless {@code getFillsViewportHeight} is 5228 * {@code true} and the table needs to be stretched to fill 5229 * the viewport 5230 * @see Scrollable#getScrollableTracksViewportHeight 5231 * @see #setFillsViewportHeight 5232 * @see #getFillsViewportHeight 5233 */ 5234 public boolean getScrollableTracksViewportHeight() { 5235 Container parent = SwingUtilities.getUnwrappedParent(this); 5236 return getFillsViewportHeight() 5237 && parent instanceof JViewport 5238 && parent.getHeight() > getPreferredSize().height; 5239 } 5240 5241 /** 5242 * Sets whether or not this table is always made large enough 5243 * to fill the height of an enclosing viewport. If the preferred 5244 * height of the table is smaller than the viewport, then the table 5245 * will be stretched to fill the viewport. In other words, this 5246 * ensures the table is never smaller than the viewport. 5247 * The default for this property is {@code false}. 5248 * 5249 * @param fillsViewportHeight whether or not this table is always 5250 * made large enough to fill the height of an enclosing 5251 * viewport 5252 * @see #getFillsViewportHeight 5253 * @see #getScrollableTracksViewportHeight 5254 * @since 1.6 5255 * @beaninfo 5256 * bound: true 5257 * description: Whether or not this table is always made large enough 5258 * to fill the height of an enclosing viewport 5259 */ 5260 public void setFillsViewportHeight(boolean fillsViewportHeight) { 5261 boolean old = this.fillsViewportHeight; 5262 this.fillsViewportHeight = fillsViewportHeight; 5263 resizeAndRepaint(); 5264 firePropertyChange("fillsViewportHeight", old, fillsViewportHeight); 5265 } 5266 5267 /** 5268 * Returns whether or not this table is always made large enough 5269 * to fill the height of an enclosing viewport. 5270 * 5271 * @return whether or not this table is always made large enough 5272 * to fill the height of an enclosing viewport 5273 * @see #setFillsViewportHeight 5274 * @since 1.6 5275 */ 5276 public boolean getFillsViewportHeight() { 5277 return fillsViewportHeight; 5278 } 5279 5280 // 5281 // Protected Methods 5282 // 5283 5284 protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, 5285 int condition, boolean pressed) { 5286 boolean retValue = super.processKeyBinding(ks, e, condition, pressed); 5287 5288 // Start editing when a key is typed. UI classes can disable this behavior 5289 // by setting the client property JTable.autoStartsEdit to Boolean.FALSE. 5290 if (!retValue && condition == WHEN_ANCESTOR_OF_FOCUSED_COMPONENT && 5291 isFocusOwner() && 5292 !Boolean.FALSE.equals(getClientProperty("JTable.autoStartsEdit"))) { 5293 // We do not have a binding for the event. 5294 Component editorComponent = getEditorComponent(); 5295 if (editorComponent == null) { 5296 // Only attempt to install the editor on a KEY_PRESSED, 5297 if (e == null || e.getID() != KeyEvent.KEY_PRESSED) { 5298 return false; 5299 } 5300 // Don't start when just a modifier is pressed 5301 int code = e.getKeyCode(); 5302 if (code == KeyEvent.VK_SHIFT || code == KeyEvent.VK_CONTROL || 5303 code == KeyEvent.VK_ALT) { 5304 return false; 5305 } 5306 // Try to install the editor 5307 int leadRow = getSelectionModel().getLeadSelectionIndex(); 5308 int leadColumn = getColumnModel().getSelectionModel(). 5309 getLeadSelectionIndex(); 5310 if (leadRow != -1 && leadColumn != -1 && !isEditing()) { 5311 if (!editCellAt(leadRow, leadColumn, e)) { 5312 return false; 5313 } 5314 } 5315 editorComponent = getEditorComponent(); 5316 if (editorComponent == null) { 5317 return false; 5318 } 5319 } 5320 // If the editorComponent is a JComponent, pass the event to it. 5321 if (editorComponent instanceof JComponent) { 5322 retValue = ((JComponent)editorComponent).processKeyBinding 5323 (ks, e, WHEN_FOCUSED, pressed); 5324 // If we have started an editor as a result of the user 5325 // pressing a key and the surrendersFocusOnKeystroke property 5326 // is true, give the focus to the new editor. 5327 if (getSurrendersFocusOnKeystroke()) { 5328 editorComponent.requestFocus(); 5329 } 5330 } 5331 } 5332 return retValue; 5333 } 5334 5335 /** 5336 * Creates default cell renderers for objects, numbers, doubles, dates, 5337 * booleans, and icons. 5338 * @see javax.swing.table.DefaultTableCellRenderer 5339 * 5340 */ 5341 protected void createDefaultRenderers() { 5342 defaultRenderersByColumnClass = new UIDefaults(8, 0.75f); 5343 5344 // Objects 5345 defaultRenderersByColumnClass.put(Object.class, (UIDefaults.LazyValue) 5346 t -> new DefaultTableCellRenderer.UIResource()); 5347 5348 // Numbers 5349 defaultRenderersByColumnClass.put(Number.class, (UIDefaults.LazyValue) 5350 t -> new NumberRenderer()); 5351 5352 // Doubles and Floats 5353 defaultRenderersByColumnClass.put(Float.class, (UIDefaults.LazyValue) 5354 t -> new DoubleRenderer()); 5355 defaultRenderersByColumnClass.put(Double.class, (UIDefaults.LazyValue) 5356 t -> new DoubleRenderer()); 5357 5358 // Dates 5359 defaultRenderersByColumnClass.put(Date.class, (UIDefaults.LazyValue) 5360 t -> new DateRenderer()); 5361 5362 // Icons and ImageIcons 5363 defaultRenderersByColumnClass.put(Icon.class, (UIDefaults.LazyValue) 5364 t -> new IconRenderer()); 5365 defaultRenderersByColumnClass.put(ImageIcon.class, (UIDefaults.LazyValue) 5366 t -> new IconRenderer()); 5367 5368 // Booleans 5369 defaultRenderersByColumnClass.put(Boolean.class, (UIDefaults.LazyValue) 5370 t -> new BooleanRenderer()); 5371 } 5372 5373 /** 5374 * Default Renderers 5375 **/ 5376 static class NumberRenderer extends DefaultTableCellRenderer.UIResource { 5377 public NumberRenderer() { 5378 super(); 5379 setHorizontalAlignment(JLabel.RIGHT); 5380 } 5381 } 5382 5383 static class DoubleRenderer extends NumberRenderer { 5384 NumberFormat formatter; 5385 public DoubleRenderer() { super(); } 5386 5387 public void setValue(Object value) { 5388 if (formatter == null) { 5389 formatter = NumberFormat.getInstance(); 5390 } 5391 setText((value == null) ? "" : formatter.format(value)); 5392 } 5393 } 5394 5395 static class DateRenderer extends DefaultTableCellRenderer.UIResource { 5396 DateFormat formatter; 5397 public DateRenderer() { super(); } 5398 5399 public void setValue(Object value) { 5400 if (formatter==null) { 5401 formatter = DateFormat.getDateInstance(); 5402 } 5403 setText((value == null) ? "" : formatter.format(value)); 5404 } 5405 } 5406 5407 static class IconRenderer extends DefaultTableCellRenderer.UIResource { 5408 public IconRenderer() { 5409 super(); 5410 setHorizontalAlignment(JLabel.CENTER); 5411 } 5412 public void setValue(Object value) { setIcon((value instanceof Icon) ? (Icon)value : null); } 5413 } 5414 5415 5416 static class BooleanRenderer extends JCheckBox implements TableCellRenderer, UIResource 5417 { 5418 private static final Border noFocusBorder = new EmptyBorder(1, 1, 1, 1); 5419 5420 public BooleanRenderer() { 5421 super(); 5422 setHorizontalAlignment(JLabel.CENTER); 5423 setBorderPainted(true); 5424 } 5425 5426 public Component getTableCellRendererComponent(JTable table, Object value, 5427 boolean isSelected, boolean hasFocus, int row, int column) { 5428 if (isSelected) { 5429 setForeground(table.getSelectionForeground()); 5430 super.setBackground(table.getSelectionBackground()); 5431 } 5432 else { 5433 setForeground(table.getForeground()); 5434 setBackground(table.getBackground()); 5435 } 5436 setSelected((value != null && ((Boolean)value).booleanValue())); 5437 5438 if (hasFocus) { 5439 setBorder(UIManager.getBorder("Table.focusCellHighlightBorder")); 5440 } else { 5441 setBorder(noFocusBorder); 5442 } 5443 5444 return this; 5445 } 5446 } 5447 5448 /** 5449 * Creates default cell editors for objects, numbers, and boolean values. 5450 * @see DefaultCellEditor 5451 */ 5452 protected void createDefaultEditors() { 5453 defaultEditorsByColumnClass = new UIDefaults(3, 0.75f); 5454 5455 // Objects 5456 defaultEditorsByColumnClass.put(Object.class, (UIDefaults.LazyValue) 5457 t -> new GenericEditor()); 5458 5459 // Numbers 5460 defaultEditorsByColumnClass.put(Number.class, (UIDefaults.LazyValue) 5461 t -> new NumberEditor()); 5462 5463 // Booleans 5464 defaultEditorsByColumnClass.put(Boolean.class, (UIDefaults.LazyValue) 5465 t -> new BooleanEditor()); 5466 } 5467 5468 /** 5469 * Default Editors 5470 */ 5471 static class GenericEditor extends DefaultCellEditor { 5472 5473 Class<?>[] argTypes = new Class<?>[]{String.class}; 5474 java.lang.reflect.Constructor<?> constructor; 5475 Object value; 5476 5477 public GenericEditor() { 5478 super(new JTextField()); 5479 getComponent().setName("Table.editor"); 5480 } 5481 5482 public boolean stopCellEditing() { 5483 String s = (String)super.getCellEditorValue(); 5484 // Here we are dealing with the case where a user 5485 // has deleted the string value in a cell, possibly 5486 // after a failed validation. Return null, so that 5487 // they have the option to replace the value with 5488 // null or use escape to restore the original. 5489 // For Strings, return "" for backward compatibility. 5490 try { 5491 if ("".equals(s)) { 5492 if (constructor.getDeclaringClass() == String.class) { 5493 value = s; 5494 } 5495 return super.stopCellEditing(); 5496 } 5497 5498 SwingUtilities2.checkAccess(constructor.getModifiers()); 5499 value = constructor.newInstance(new Object[]{s}); 5500 } 5501 catch (Exception e) { 5502 ((JComponent)getComponent()).setBorder(new LineBorder(Color.red)); 5503 return false; 5504 } 5505 return super.stopCellEditing(); 5506 } 5507 5508 public Component getTableCellEditorComponent(JTable table, Object value, 5509 boolean isSelected, 5510 int row, int column) { 5511 this.value = null; 5512 ((JComponent)getComponent()).setBorder(new LineBorder(Color.black)); 5513 try { 5514 Class<?> type = table.getColumnClass(column); 5515 // Since our obligation is to produce a value which is 5516 // assignable for the required type it is OK to use the 5517 // String constructor for columns which are declared 5518 // to contain Objects. A String is an Object. 5519 if (type == Object.class) { 5520 type = String.class; 5521 } 5522 ReflectUtil.checkPackageAccess(type); 5523 SwingUtilities2.checkAccess(type.getModifiers()); 5524 constructor = type.getConstructor(argTypes); 5525 } 5526 catch (Exception e) { 5527 return null; 5528 } 5529 return super.getTableCellEditorComponent(table, value, isSelected, row, column); 5530 } 5531 5532 public Object getCellEditorValue() { 5533 return value; 5534 } 5535 } 5536 5537 static class NumberEditor extends GenericEditor { 5538 5539 public NumberEditor() { 5540 ((JTextField)getComponent()).setHorizontalAlignment(JTextField.RIGHT); 5541 } 5542 } 5543 5544 static class BooleanEditor extends DefaultCellEditor { 5545 public BooleanEditor() { 5546 super(new JCheckBox()); 5547 JCheckBox checkBox = (JCheckBox)getComponent(); 5548 checkBox.setHorizontalAlignment(JCheckBox.CENTER); 5549 } 5550 } 5551 5552 /** 5553 * Initializes table properties to their default values. 5554 */ 5555 protected void initializeLocalVars() { 5556 updateSelectionOnSort = true; 5557 setOpaque(true); 5558 createDefaultRenderers(); 5559 createDefaultEditors(); 5560 5561 setTableHeader(createDefaultTableHeader()); 5562 5563 setShowGrid(true); 5564 setAutoResizeMode(AUTO_RESIZE_SUBSEQUENT_COLUMNS); 5565 setRowHeight(16); 5566 isRowHeightSet = false; 5567 setRowMargin(1); 5568 setRowSelectionAllowed(true); 5569 setCellEditor(null); 5570 setEditingColumn(-1); 5571 setEditingRow(-1); 5572 setSurrendersFocusOnKeystroke(false); 5573 setPreferredScrollableViewportSize(new Dimension(450, 400)); 5574 5575 // I'm registered to do tool tips so we can draw tips for the renderers 5576 ToolTipManager toolTipManager = ToolTipManager.sharedInstance(); 5577 toolTipManager.registerComponent(this); 5578 5579 setAutoscrolls(true); 5580 } 5581 5582 /** 5583 * Returns the default table model object, which is 5584 * a <code>DefaultTableModel</code>. A subclass can override this 5585 * method to return a different table model object. 5586 * 5587 * @return the default table model object 5588 * @see javax.swing.table.DefaultTableModel 5589 */ 5590 protected TableModel createDefaultDataModel() { 5591 return new DefaultTableModel(); 5592 } 5593 5594 /** 5595 * Returns the default column model object, which is 5596 * a <code>DefaultTableColumnModel</code>. A subclass can override this 5597 * method to return a different column model object. 5598 * 5599 * @return the default column model object 5600 * @see javax.swing.table.DefaultTableColumnModel 5601 */ 5602 protected TableColumnModel createDefaultColumnModel() { 5603 return new DefaultTableColumnModel(); 5604 } 5605 5606 /** 5607 * Returns the default selection model object, which is 5608 * a <code>DefaultListSelectionModel</code>. A subclass can override this 5609 * method to return a different selection model object. 5610 * 5611 * @return the default selection model object 5612 * @see javax.swing.DefaultListSelectionModel 5613 */ 5614 protected ListSelectionModel createDefaultSelectionModel() { 5615 return new DefaultListSelectionModel(); 5616 } 5617 5618 /** 5619 * Returns the default table header object, which is 5620 * a <code>JTableHeader</code>. A subclass can override this 5621 * method to return a different table header object. 5622 * 5623 * @return the default table header object 5624 * @see javax.swing.table.JTableHeader 5625 */ 5626 protected JTableHeader createDefaultTableHeader() { 5627 return new JTableHeader(columnModel); 5628 } 5629 5630 /** 5631 * Equivalent to <code>revalidate</code> followed by <code>repaint</code>. 5632 */ 5633 protected void resizeAndRepaint() { 5634 revalidate(); 5635 repaint(); 5636 } 5637 5638 /** 5639 * Returns the active cell editor, which is {@code null} if the table 5640 * is not currently editing. 5641 * 5642 * @return the {@code TableCellEditor} that does the editing, 5643 * or {@code null} if the table is not currently editing. 5644 * @see #cellEditor 5645 * @see #getCellEditor(int, int) 5646 */ 5647 public TableCellEditor getCellEditor() { 5648 return cellEditor; 5649 } 5650 5651 /** 5652 * Sets the active cell editor. 5653 * 5654 * @param anEditor the active cell editor 5655 * @see #cellEditor 5656 * @beaninfo 5657 * bound: true 5658 * description: The table's active cell editor. 5659 */ 5660 public void setCellEditor(TableCellEditor anEditor) { 5661 TableCellEditor oldEditor = cellEditor; 5662 cellEditor = anEditor; 5663 firePropertyChange("tableCellEditor", oldEditor, anEditor); 5664 } 5665 5666 /** 5667 * Sets the <code>editingColumn</code> variable. 5668 * @param aColumn the column of the cell to be edited 5669 * 5670 * @see #editingColumn 5671 */ 5672 public void setEditingColumn(int aColumn) { 5673 editingColumn = aColumn; 5674 } 5675 5676 /** 5677 * Sets the <code>editingRow</code> variable. 5678 * @param aRow the row of the cell to be edited 5679 * 5680 * @see #editingRow 5681 */ 5682 public void setEditingRow(int aRow) { 5683 editingRow = aRow; 5684 } 5685 5686 /** 5687 * Returns an appropriate renderer for the cell specified by this row and 5688 * column. If the <code>TableColumn</code> for this column has a non-null 5689 * renderer, returns that. If not, finds the class of the data in 5690 * this column (using <code>getColumnClass</code>) 5691 * and returns the default renderer for this type of data. 5692 * <p> 5693 * <b>Note:</b> 5694 * Throughout the table package, the internal implementations always 5695 * use this method to provide renderers so that this default behavior 5696 * can be safely overridden by a subclass. 5697 * 5698 * @param row the row of the cell to render, where 0 is the first row 5699 * @param column the column of the cell to render, 5700 * where 0 is the first column 5701 * @return the assigned renderer; if <code>null</code> 5702 * returns the default renderer 5703 * for this type of object 5704 * @see javax.swing.table.DefaultTableCellRenderer 5705 * @see javax.swing.table.TableColumn#setCellRenderer 5706 * @see #setDefaultRenderer 5707 */ 5708 public TableCellRenderer getCellRenderer(int row, int column) { 5709 TableColumn tableColumn = getColumnModel().getColumn(column); 5710 TableCellRenderer renderer = tableColumn.getCellRenderer(); 5711 if (renderer == null) { 5712 renderer = getDefaultRenderer(getColumnClass(column)); 5713 } 5714 return renderer; 5715 } 5716 5717 /** 5718 * Prepares the renderer by querying the data model for the 5719 * value and selection state 5720 * of the cell at <code>row</code>, <code>column</code>. 5721 * Returns the component (may be a <code>Component</code> 5722 * or a <code>JComponent</code>) under the event location. 5723 * <p> 5724 * During a printing operation, this method will configure the 5725 * renderer without indicating selection or focus, to prevent 5726 * them from appearing in the printed output. To do other 5727 * customizations based on whether or not the table is being 5728 * printed, you can check the value of 5729 * {@link javax.swing.JComponent#isPaintingForPrint()}, either here 5730 * or within custom renderers. 5731 * <p> 5732 * <b>Note:</b> 5733 * Throughout the table package, the internal implementations always 5734 * use this method to prepare renderers so that this default behavior 5735 * can be safely overridden by a subclass. 5736 * 5737 * @param renderer the <code>TableCellRenderer</code> to prepare 5738 * @param row the row of the cell to render, where 0 is the first row 5739 * @param column the column of the cell to render, 5740 * where 0 is the first column 5741 * @return the <code>Component</code> under the event location 5742 */ 5743 public Component prepareRenderer(TableCellRenderer renderer, int row, int column) { 5744 Object value = getValueAt(row, column); 5745 5746 boolean isSelected = false; 5747 boolean hasFocus = false; 5748 5749 // Only indicate the selection and focused cell if not printing 5750 if (!isPaintingForPrint()) { 5751 isSelected = isCellSelected(row, column); 5752 5753 boolean rowIsLead = 5754 (selectionModel.getLeadSelectionIndex() == row); 5755 boolean colIsLead = 5756 (columnModel.getSelectionModel().getLeadSelectionIndex() == column); 5757 5758 hasFocus = (rowIsLead && colIsLead) && isFocusOwner(); 5759 } 5760 5761 return renderer.getTableCellRendererComponent(this, value, 5762 isSelected, hasFocus, 5763 row, column); 5764 } 5765 5766 /** 5767 * Returns an appropriate editor for the cell specified by 5768 * <code>row</code> and <code>column</code>. If the 5769 * <code>TableColumn</code> for this column has a non-null editor, 5770 * returns that. If not, finds the class of the data in this 5771 * column (using <code>getColumnClass</code>) 5772 * and returns the default editor for this type of data. 5773 * <p> 5774 * <b>Note:</b> 5775 * Throughout the table package, the internal implementations always 5776 * use this method to provide editors so that this default behavior 5777 * can be safely overridden by a subclass. 5778 * 5779 * @param row the row of the cell to edit, where 0 is the first row 5780 * @param column the column of the cell to edit, 5781 * where 0 is the first column 5782 * @return the editor for this cell; 5783 * if <code>null</code> return the default editor for 5784 * this type of cell 5785 * @see DefaultCellEditor 5786 */ 5787 public TableCellEditor getCellEditor(int row, int column) { 5788 TableColumn tableColumn = getColumnModel().getColumn(column); 5789 TableCellEditor editor = tableColumn.getCellEditor(); 5790 if (editor == null) { 5791 editor = getDefaultEditor(getColumnClass(column)); 5792 } 5793 return editor; 5794 } 5795 5796 5797 /** 5798 * Prepares the editor by querying the data model for the value and 5799 * selection state of the cell at <code>row</code>, <code>column</code>. 5800 * <p> 5801 * <b>Note:</b> 5802 * Throughout the table package, the internal implementations always 5803 * use this method to prepare editors so that this default behavior 5804 * can be safely overridden by a subclass. 5805 * 5806 * @param editor the <code>TableCellEditor</code> to set up 5807 * @param row the row of the cell to edit, 5808 * where 0 is the first row 5809 * @param column the column of the cell to edit, 5810 * where 0 is the first column 5811 * @return the <code>Component</code> being edited 5812 */ 5813 @SuppressWarnings("deprecation") 5814 public Component prepareEditor(TableCellEditor editor, int row, int column) { 5815 Object value = getValueAt(row, column); 5816 boolean isSelected = isCellSelected(row, column); 5817 Component comp = editor.getTableCellEditorComponent(this, value, isSelected, 5818 row, column); 5819 if (comp instanceof JComponent) { 5820 JComponent jComp = (JComponent)comp; 5821 if (jComp.getNextFocusableComponent() == null) { 5822 jComp.setNextFocusableComponent(this); 5823 } 5824 } 5825 return comp; 5826 } 5827 5828 /** 5829 * Discards the editor object and frees the real estate it used for 5830 * cell rendering. 5831 */ 5832 public void removeEditor() { 5833 KeyboardFocusManager.getCurrentKeyboardFocusManager(). 5834 removePropertyChangeListener("permanentFocusOwner", editorRemover); 5835 editorRemover = null; 5836 5837 TableCellEditor editor = getCellEditor(); 5838 if(editor != null) { 5839 editor.removeCellEditorListener(this); 5840 if (editorComp != null) { 5841 Component focusOwner = 5842 KeyboardFocusManager.getCurrentKeyboardFocusManager().getFocusOwner(); 5843 boolean isFocusOwnerInTheTable = focusOwner != null? 5844 SwingUtilities.isDescendingFrom(focusOwner, this):false; 5845 remove(editorComp); 5846 if(isFocusOwnerInTheTable) { 5847 requestFocusInWindow(); 5848 } 5849 } 5850 5851 Rectangle cellRect = getCellRect(editingRow, editingColumn, false); 5852 5853 setCellEditor(null); 5854 setEditingColumn(-1); 5855 setEditingRow(-1); 5856 editorComp = null; 5857 5858 repaint(cellRect); 5859 } 5860 } 5861 5862 // 5863 // Serialization 5864 // 5865 5866 /** 5867 * See readObject() and writeObject() in JComponent for more 5868 * information about serialization in Swing. 5869 */ 5870 private void writeObject(ObjectOutputStream s) throws IOException { 5871 s.defaultWriteObject(); 5872 if (getUIClassID().equals(uiClassID)) { 5873 byte count = JComponent.getWriteObjCounter(this); 5874 JComponent.setWriteObjCounter(this, --count); 5875 if (count == 0 && ui != null) { 5876 ui.installUI(this); 5877 } 5878 } 5879 } 5880 5881 private void readObject(ObjectInputStream s) 5882 throws IOException, ClassNotFoundException 5883 { 5884 ObjectInputStream.GetField f = s.readFields(); 5885 5886 TableModel newDataModel = (TableModel) f.get("dataModel", null); 5887 if (newDataModel == null) { 5888 throw new InvalidObjectException("Null dataModel"); 5889 } 5890 dataModel = newDataModel; 5891 5892 TableColumnModel newColumnModel = (TableColumnModel) f.get("columnModel", null); 5893 if (newColumnModel == null) { 5894 throw new InvalidObjectException("Null columnModel"); 5895 } 5896 columnModel = newColumnModel; 5897 5898 ListSelectionModel newSelectionModel = (ListSelectionModel) f.get("selectionModel", null); 5899 if (newSelectionModel == null) { 5900 throw new InvalidObjectException("Null selectionModel"); 5901 } 5902 selectionModel = newSelectionModel; 5903 5904 tableHeader = (JTableHeader) f.get("tableHeader", null); 5905 int newRowHeight = f.get("rowHeight", 0); 5906 if (newRowHeight <= 0) { 5907 throw new InvalidObjectException("Row height less than 1"); 5908 } 5909 rowHeight = newRowHeight; 5910 5911 rowMargin = f.get("rowMargin", 0); 5912 Color newGridColor = (Color) f.get("gridColor", null); 5913 if (newGridColor == null) { 5914 throw new InvalidObjectException("Null gridColor"); 5915 } 5916 gridColor = newGridColor; 5917 5918 showHorizontalLines = f.get("showHorizontalLines", false); 5919 showVerticalLines = f.get("showVerticalLines", false); 5920 int newAutoResizeMode = f.get("autoResizeMode", 0); 5921 if (!isValidAutoResizeMode(newAutoResizeMode)) { 5922 throw new InvalidObjectException("autoResizeMode is not valid"); 5923 } 5924 autoResizeMode = newAutoResizeMode; 5925 autoCreateColumnsFromModel = f.get("autoCreateColumnsFromModel", false); 5926 preferredViewportSize = (Dimension) f.get("preferredViewportSize", null); 5927 rowSelectionAllowed = f.get("rowSelectionAllowed", false); 5928 cellSelectionEnabled = f.get("cellSelectionEnabled", false); 5929 selectionForeground = (Color) f.get("selectionForeground", null); 5930 selectionBackground = (Color) f.get("selectionBackground", null); 5931 rowModel = (SizeSequence) f.get("rowModel", null); 5932 5933 boolean newDragEnabled = f.get("dragEnabled", false); 5934 checkDragEnabled(newDragEnabled); 5935 dragEnabled = newDragEnabled; 5936 5937 surrendersFocusOnKeystroke = f.get("surrendersFocusOnKeystroke", false); 5938 editorRemover = (PropertyChangeListener) f.get("editorRemover", null); 5939 columnSelectionAdjusting = f.get("columnSelectionAdjusting", false); 5940 rowSelectionAdjusting = f.get("rowSelectionAdjusting", false); 5941 printError = (Throwable) f.get("printError", null); 5942 isRowHeightSet = f.get("isRowHeightSet", false); 5943 updateSelectionOnSort = f.get("updateSelectionOnSort", false); 5944 ignoreSortChange = f.get("ignoreSortChange", false); 5945 sorterChanged = f.get("sorterChanged", false); 5946 autoCreateRowSorter = f.get("autoCreateRowSorter", false); 5947 fillsViewportHeight = f.get("fillsViewportHeight", false); 5948 DropMode newDropMode = (DropMode) f.get("dropMode", 5949 DropMode.USE_SELECTION); 5950 checkDropMode(newDropMode); 5951 dropMode = newDropMode; 5952 5953 if ((ui != null) && (getUIClassID().equals(uiClassID))) { 5954 ui.installUI(this); 5955 } 5956 createDefaultRenderers(); 5957 createDefaultEditors(); 5958 5959 // If ToolTipText != null, then the tooltip has already been 5960 // registered by JComponent.readObject() and we don't want 5961 // to re-register here 5962 if (getToolTipText() == null) { 5963 ToolTipManager.sharedInstance().registerComponent(this); 5964 } 5965 } 5966 5967 /* Called from the JComponent's EnableSerializationFocusListener to 5968 * do any Swing-specific pre-serialization configuration. 5969 */ 5970 void compWriteObjectNotify() { 5971 super.compWriteObjectNotify(); 5972 // If ToolTipText != null, then the tooltip has already been 5973 // unregistered by JComponent.compWriteObjectNotify() 5974 if (getToolTipText() == null) { 5975 ToolTipManager.sharedInstance().unregisterComponent(this); 5976 } 5977 } 5978 5979 /** 5980 * Returns a string representation of this table. This method 5981 * is intended to be used only for debugging purposes, and the 5982 * content and format of the returned string may vary between 5983 * implementations. The returned string may be empty but may not 5984 * be <code>null</code>. 5985 * 5986 * @return a string representation of this table 5987 */ 5988 protected String paramString() { 5989 String gridColorString = (gridColor != null ? 5990 gridColor.toString() : ""); 5991 String showHorizontalLinesString = (showHorizontalLines ? 5992 "true" : "false"); 5993 String showVerticalLinesString = (showVerticalLines ? 5994 "true" : "false"); 5995 String autoResizeModeString; 5996 if (autoResizeMode == AUTO_RESIZE_OFF) { 5997 autoResizeModeString = "AUTO_RESIZE_OFF"; 5998 } else if (autoResizeMode == AUTO_RESIZE_NEXT_COLUMN) { 5999 autoResizeModeString = "AUTO_RESIZE_NEXT_COLUMN"; 6000 } else if (autoResizeMode == AUTO_RESIZE_SUBSEQUENT_COLUMNS) { 6001 autoResizeModeString = "AUTO_RESIZE_SUBSEQUENT_COLUMNS"; 6002 } else if (autoResizeMode == AUTO_RESIZE_LAST_COLUMN) { 6003 autoResizeModeString = "AUTO_RESIZE_LAST_COLUMN"; 6004 } else if (autoResizeMode == AUTO_RESIZE_ALL_COLUMNS) { 6005 autoResizeModeString = "AUTO_RESIZE_ALL_COLUMNS"; 6006 } else autoResizeModeString = ""; 6007 String autoCreateColumnsFromModelString = (autoCreateColumnsFromModel ? 6008 "true" : "false"); 6009 String preferredViewportSizeString = (preferredViewportSize != null ? 6010 preferredViewportSize.toString() 6011 : ""); 6012 String rowSelectionAllowedString = (rowSelectionAllowed ? 6013 "true" : "false"); 6014 String cellSelectionEnabledString = (cellSelectionEnabled ? 6015 "true" : "false"); 6016 String selectionForegroundString = (selectionForeground != null ? 6017 selectionForeground.toString() : 6018 ""); 6019 String selectionBackgroundString = (selectionBackground != null ? 6020 selectionBackground.toString() : 6021 ""); 6022 6023 return super.paramString() + 6024 ",autoCreateColumnsFromModel=" + autoCreateColumnsFromModelString + 6025 ",autoResizeMode=" + autoResizeModeString + 6026 ",cellSelectionEnabled=" + cellSelectionEnabledString + 6027 ",editingColumn=" + editingColumn + 6028 ",editingRow=" + editingRow + 6029 ",gridColor=" + gridColorString + 6030 ",preferredViewportSize=" + preferredViewportSizeString + 6031 ",rowHeight=" + rowHeight + 6032 ",rowMargin=" + rowMargin + 6033 ",rowSelectionAllowed=" + rowSelectionAllowedString + 6034 ",selectionBackground=" + selectionBackgroundString + 6035 ",selectionForeground=" + selectionForegroundString + 6036 ",showHorizontalLines=" + showHorizontalLinesString + 6037 ",showVerticalLines=" + showVerticalLinesString; 6038 } 6039 6040 // This class tracks changes in the keyboard focus state. It is used 6041 // when the JTable is editing to determine when to cancel the edit. 6042 // If focus switches to a component outside of the jtable, but in the 6043 // same window, this will cancel editing. 6044 class CellEditorRemover implements PropertyChangeListener { 6045 KeyboardFocusManager focusManager; 6046 6047 public CellEditorRemover(KeyboardFocusManager fm) { 6048 this.focusManager = fm; 6049 } 6050 6051 public void propertyChange(PropertyChangeEvent ev) { 6052 if (!isEditing() || getClientProperty("terminateEditOnFocusLost") != Boolean.TRUE) { 6053 return; 6054 } 6055 6056 Component c = focusManager.getPermanentFocusOwner(); 6057 while (c != null) { 6058 if (c == JTable.this) { 6059 // focus remains inside the table 6060 return; 6061 } else if ((c instanceof Window) || 6062 (c instanceof Applet && c.getParent() == null)) { 6063 if (c == SwingUtilities.getRoot(JTable.this)) { 6064 if (!getCellEditor().stopCellEditing()) { 6065 getCellEditor().cancelCellEditing(); 6066 } 6067 } 6068 break; 6069 } 6070 c = c.getParent(); 6071 } 6072 } 6073 } 6074 6075 ///////////////// 6076 // Printing Support 6077 ///////////////// 6078 6079 /** 6080 * A convenience method that displays a printing dialog, and then prints 6081 * this <code>JTable</code> in mode <code>PrintMode.FIT_WIDTH</code>, 6082 * with no header or footer text. A modal progress dialog, with an abort 6083 * option, will be shown for the duration of printing. 6084 * <p> 6085 * Note: In headless mode, no dialogs are shown and printing 6086 * occurs on the default printer. 6087 * 6088 * @return true, unless printing is cancelled by the user 6089 * @throws SecurityException if this thread is not allowed to 6090 * initiate a print job request 6091 * @throws PrinterException if an error in the print system causes the job 6092 * to be aborted 6093 * @see #print(JTable.PrintMode, MessageFormat, MessageFormat, 6094 * boolean, PrintRequestAttributeSet, boolean, PrintService) 6095 * @see #getPrintable 6096 * 6097 * @since 1.5 6098 */ 6099 public boolean print() throws PrinterException { 6100 6101 return print(PrintMode.FIT_WIDTH); 6102 } 6103 6104 /** 6105 * A convenience method that displays a printing dialog, and then prints 6106 * this <code>JTable</code> in the given printing mode, 6107 * with no header or footer text. A modal progress dialog, with an abort 6108 * option, will be shown for the duration of printing. 6109 * <p> 6110 * Note: In headless mode, no dialogs are shown and printing 6111 * occurs on the default printer. 6112 * 6113 * @param printMode the printing mode that the printable should use 6114 * @return true, unless printing is cancelled by the user 6115 * @throws SecurityException if this thread is not allowed to 6116 * initiate a print job request 6117 * @throws PrinterException if an error in the print system causes the job 6118 * to be aborted 6119 * @see #print(JTable.PrintMode, MessageFormat, MessageFormat, 6120 * boolean, PrintRequestAttributeSet, boolean, PrintService) 6121 * @see #getPrintable 6122 * 6123 * @since 1.5 6124 */ 6125 public boolean print(PrintMode printMode) throws PrinterException { 6126 6127 return print(printMode, null, null); 6128 } 6129 6130 /** 6131 * A convenience method that displays a printing dialog, and then prints 6132 * this <code>JTable</code> in the given printing mode, 6133 * with the specified header and footer text. A modal progress dialog, 6134 * with an abort option, will be shown for the duration of printing. 6135 * <p> 6136 * Note: In headless mode, no dialogs are shown and printing 6137 * occurs on the default printer. 6138 * 6139 * @param printMode the printing mode that the printable should use 6140 * @param headerFormat a <code>MessageFormat</code> specifying the text 6141 * to be used in printing a header, 6142 * or null for none 6143 * @param footerFormat a <code>MessageFormat</code> specifying the text 6144 * to be used in printing a footer, 6145 * or null for none 6146 * @return true, unless printing is cancelled by the user 6147 * @throws SecurityException if this thread is not allowed to 6148 * initiate a print job request 6149 * @throws PrinterException if an error in the print system causes the job 6150 * to be aborted 6151 * @see #print(JTable.PrintMode, MessageFormat, MessageFormat, 6152 * boolean, PrintRequestAttributeSet, boolean, PrintService) 6153 * @see #getPrintable 6154 * 6155 * @since 1.5 6156 */ 6157 public boolean print(PrintMode printMode, 6158 MessageFormat headerFormat, 6159 MessageFormat footerFormat) throws PrinterException { 6160 6161 boolean showDialogs = !GraphicsEnvironment.isHeadless(); 6162 return print(printMode, headerFormat, footerFormat, 6163 showDialogs, null, showDialogs); 6164 } 6165 6166 /** 6167 * Prints this table, as specified by the fully featured 6168 * {@link #print(JTable.PrintMode, MessageFormat, MessageFormat, 6169 * boolean, PrintRequestAttributeSet, boolean, PrintService) print} 6170 * method, with the default printer specified as the print service. 6171 * 6172 * @param printMode the printing mode that the printable should use 6173 * @param headerFormat a <code>MessageFormat</code> specifying the text 6174 * to be used in printing a header, 6175 * or <code>null</code> for none 6176 * @param footerFormat a <code>MessageFormat</code> specifying the text 6177 * to be used in printing a footer, 6178 * or <code>null</code> for none 6179 * @param showPrintDialog whether or not to display a print dialog 6180 * @param attr a <code>PrintRequestAttributeSet</code> 6181 * specifying any printing attributes, 6182 * or <code>null</code> for none 6183 * @param interactive whether or not to print in an interactive mode 6184 * @return true, unless printing is cancelled by the user 6185 * @throws HeadlessException if the method is asked to show a printing 6186 * dialog or run interactively, and 6187 * <code>GraphicsEnvironment.isHeadless</code> 6188 * returns <code>true</code> 6189 * @throws SecurityException if this thread is not allowed to 6190 * initiate a print job request 6191 * @throws PrinterException if an error in the print system causes the job 6192 * to be aborted 6193 * @see #print(JTable.PrintMode, MessageFormat, MessageFormat, 6194 * boolean, PrintRequestAttributeSet, boolean, PrintService) 6195 * @see #getPrintable 6196 * 6197 * @since 1.5 6198 */ 6199 public boolean print(PrintMode printMode, 6200 MessageFormat headerFormat, 6201 MessageFormat footerFormat, 6202 boolean showPrintDialog, 6203 PrintRequestAttributeSet attr, 6204 boolean interactive) throws PrinterException, 6205 HeadlessException { 6206 6207 return print(printMode, 6208 headerFormat, 6209 footerFormat, 6210 showPrintDialog, 6211 attr, 6212 interactive, 6213 null); 6214 } 6215 6216 /** 6217 * Prints this <code>JTable</code>. Takes steps that the majority of 6218 * developers would take in order to print a <code>JTable</code>. 6219 * In short, it prepares the table, calls <code>getPrintable</code> to 6220 * fetch an appropriate <code>Printable</code>, and then sends it to the 6221 * printer. 6222 * <p> 6223 * A <code>boolean</code> parameter allows you to specify whether or not 6224 * a printing dialog is displayed to the user. When it is, the user may 6225 * use the dialog to change the destination printer or printing attributes, 6226 * or even to cancel the print. Another two parameters allow for a 6227 * <code>PrintService</code> and printing attributes to be specified. 6228 * These parameters can be used either to provide initial values for the 6229 * print dialog, or to specify values when the dialog is not shown. 6230 * <p> 6231 * A second <code>boolean</code> parameter allows you to specify whether 6232 * or not to perform printing in an interactive mode. If <code>true</code>, 6233 * a modal progress dialog, with an abort option, is displayed for the 6234 * duration of printing . This dialog also prevents any user action which 6235 * may affect the table. However, it can not prevent the table from being 6236 * modified by code (for example, another thread that posts updates using 6237 * <code>SwingUtilities.invokeLater</code>). It is therefore the 6238 * responsibility of the developer to ensure that no other code modifies 6239 * the table in any way during printing (invalid modifications include 6240 * changes in: size, renderers, or underlying data). Printing behavior is 6241 * undefined when the table is changed during printing. 6242 * <p> 6243 * If <code>false</code> is specified for this parameter, no dialog will 6244 * be displayed and printing will begin immediately on the event-dispatch 6245 * thread. This blocks any other events, including repaints, from being 6246 * processed until printing is complete. Although this effectively prevents 6247 * the table from being changed, it doesn't provide a good user experience. 6248 * For this reason, specifying <code>false</code> is only recommended when 6249 * printing from an application with no visible GUI. 6250 * <p> 6251 * Note: Attempting to show the printing dialog or run interactively, while 6252 * in headless mode, will result in a <code>HeadlessException</code>. 6253 * <p> 6254 * Before fetching the printable, this method will gracefully terminate 6255 * editing, if necessary, to prevent an editor from showing in the printed 6256 * result. Additionally, <code>JTable</code> will prepare its renderers 6257 * during printing such that selection and focus are not indicated. 6258 * As far as customizing further how the table looks in the printout, 6259 * developers can provide custom renderers or paint code that conditionalize 6260 * on the value of {@link javax.swing.JComponent#isPaintingForPrint()}. 6261 * <p> 6262 * See {@link #getPrintable} for more description on how the table is 6263 * printed. 6264 * 6265 * @param printMode the printing mode that the printable should use 6266 * @param headerFormat a <code>MessageFormat</code> specifying the text 6267 * to be used in printing a header, 6268 * or <code>null</code> for none 6269 * @param footerFormat a <code>MessageFormat</code> specifying the text 6270 * to be used in printing a footer, 6271 * or <code>null</code> for none 6272 * @param showPrintDialog whether or not to display a print dialog 6273 * @param attr a <code>PrintRequestAttributeSet</code> 6274 * specifying any printing attributes, 6275 * or <code>null</code> for none 6276 * @param interactive whether or not to print in an interactive mode 6277 * @param service the destination <code>PrintService</code>, 6278 * or <code>null</code> to use the default printer 6279 * @return true, unless printing is cancelled by the user 6280 * @throws HeadlessException if the method is asked to show a printing 6281 * dialog or run interactively, and 6282 * <code>GraphicsEnvironment.isHeadless</code> 6283 * returns <code>true</code> 6284 * @throws SecurityException if a security manager exists and its 6285 * {@link java.lang.SecurityManager#checkPrintJobAccess} 6286 * method disallows this thread from creating a print job request 6287 * @throws PrinterException if an error in the print system causes the job 6288 * to be aborted 6289 * @see #getPrintable 6290 * @see java.awt.GraphicsEnvironment#isHeadless 6291 * 6292 * @since 1.6 6293 */ 6294 public boolean print(PrintMode printMode, 6295 MessageFormat headerFormat, 6296 MessageFormat footerFormat, 6297 boolean showPrintDialog, 6298 PrintRequestAttributeSet attr, 6299 boolean interactive, 6300 PrintService service) throws PrinterException, 6301 HeadlessException { 6302 6303 // complain early if an invalid parameter is specified for headless mode 6304 boolean isHeadless = GraphicsEnvironment.isHeadless(); 6305 if (isHeadless) { 6306 if (showPrintDialog) { 6307 throw new HeadlessException("Can't show print dialog."); 6308 } 6309 6310 if (interactive) { 6311 throw new HeadlessException("Can't run interactively."); 6312 } 6313 } 6314 6315 // Get a PrinterJob. 6316 // Do this before anything with side-effects since it may throw a 6317 // security exception - in which case we don't want to do anything else. 6318 final PrinterJob job = PrinterJob.getPrinterJob(); 6319 6320 if (isEditing()) { 6321 // try to stop cell editing, and failing that, cancel it 6322 if (!getCellEditor().stopCellEditing()) { 6323 getCellEditor().cancelCellEditing(); 6324 } 6325 } 6326 6327 if (attr == null) { 6328 attr = new HashPrintRequestAttributeSet(); 6329 } 6330 6331 final PrintingStatus printingStatus; 6332 6333 // fetch the Printable 6334 Printable printable = 6335 getPrintable(printMode, headerFormat, footerFormat); 6336 6337 if (interactive) { 6338 // wrap the Printable so that we can print on another thread 6339 printable = new ThreadSafePrintable(printable); 6340 printingStatus = PrintingStatus.createPrintingStatus(this, job); 6341 printable = printingStatus.createNotificationPrintable(printable); 6342 } else { 6343 // to please compiler 6344 printingStatus = null; 6345 } 6346 6347 // set the printable on the PrinterJob 6348 job.setPrintable(printable); 6349 6350 // if specified, set the PrintService on the PrinterJob 6351 if (service != null) { 6352 job.setPrintService(service); 6353 } 6354 6355 // if requested, show the print dialog 6356 if (showPrintDialog && !job.printDialog(attr)) { 6357 // the user cancelled the print dialog 6358 return false; 6359 } 6360 6361 // if not interactive, just print on this thread (no dialog) 6362 if (!interactive) { 6363 // do the printing 6364 job.print(attr); 6365 6366 // we're done 6367 return true; 6368 } 6369 6370 // make sure this is clear since we'll check it after 6371 printError = null; 6372 6373 // to synchronize on 6374 final Object lock = new Object(); 6375 6376 // copied so we can access from the inner class 6377 final PrintRequestAttributeSet copyAttr = attr; 6378 6379 // this runnable will be used to do the printing 6380 // (and save any throwables) on another thread 6381 Runnable runnable = new Runnable() { 6382 public void run() { 6383 try { 6384 // do the printing 6385 job.print(copyAttr); 6386 } catch (Throwable t) { 6387 // save any Throwable to be rethrown 6388 synchronized(lock) { 6389 printError = t; 6390 } 6391 } finally { 6392 // we're finished - hide the dialog 6393 printingStatus.dispose(); 6394 } 6395 } 6396 }; 6397 6398 // start printing on another thread 6399 Thread th = new Thread(runnable); 6400 th.start(); 6401 6402 printingStatus.showModal(true); 6403 6404 // look for any error that the printing may have generated 6405 Throwable pe; 6406 synchronized(lock) { 6407 pe = printError; 6408 printError = null; 6409 } 6410 6411 // check the type of error and handle it 6412 if (pe != null) { 6413 // a subclass of PrinterException meaning the job was aborted, 6414 // in this case, by the user 6415 if (pe instanceof PrinterAbortException) { 6416 return false; 6417 } else if (pe instanceof PrinterException) { 6418 throw (PrinterException)pe; 6419 } else if (pe instanceof RuntimeException) { 6420 throw (RuntimeException)pe; 6421 } else if (pe instanceof Error) { 6422 throw (Error)pe; 6423 } 6424 6425 // can not happen 6426 throw new AssertionError(pe); 6427 } 6428 6429 return true; 6430 } 6431 6432 /** 6433 * Return a <code>Printable</code> for use in printing this JTable. 6434 * <p> 6435 * This method is meant for those wishing to customize the default 6436 * <code>Printable</code> implementation used by <code>JTable</code>'s 6437 * <code>print</code> methods. Developers wanting simply to print the table 6438 * should use one of those methods directly. 6439 * <p> 6440 * The <code>Printable</code> can be requested in one of two printing modes. 6441 * In both modes, it spreads table rows naturally in sequence across 6442 * multiple pages, fitting as many rows as possible per page. 6443 * <code>PrintMode.NORMAL</code> specifies that the table be 6444 * printed at its current size. In this mode, there may be a need to spread 6445 * columns across pages in a similar manner to that of the rows. When the 6446 * need arises, columns are distributed in an order consistent with the 6447 * table's <code>ComponentOrientation</code>. 6448 * <code>PrintMode.FIT_WIDTH</code> specifies that the output be 6449 * scaled smaller, if necessary, to fit the table's entire width 6450 * (and thereby all columns) on each page. Width and height are scaled 6451 * equally, maintaining the aspect ratio of the output. 6452 * <p> 6453 * The <code>Printable</code> heads the portion of table on each page 6454 * with the appropriate section from the table's <code>JTableHeader</code>, 6455 * if it has one. 6456 * <p> 6457 * Header and footer text can be added to the output by providing 6458 * <code>MessageFormat</code> arguments. The printing code requests 6459 * Strings from the formats, providing a single item which may be included 6460 * in the formatted string: an <code>Integer</code> representing the current 6461 * page number. 6462 * <p> 6463 * You are encouraged to read the documentation for 6464 * <code>MessageFormat</code> as some characters, such as single-quote, 6465 * are special and need to be escaped. 6466 * <p> 6467 * Here's an example of creating a <code>MessageFormat</code> that can be 6468 * used to print "Duke's Table: Page - " and the current page number: 6469 * 6470 * <pre> 6471 * // notice the escaping of the single quote 6472 * // notice how the page number is included with "{0}" 6473 * MessageFormat format = new MessageFormat("Duke''s Table: Page - {0}"); 6474 * </pre> 6475 * <p> 6476 * The <code>Printable</code> constrains what it draws to the printable 6477 * area of each page that it prints. Under certain circumstances, it may 6478 * find it impossible to fit all of a page's content into that area. In 6479 * these cases the output may be clipped, but the implementation 6480 * makes an effort to do something reasonable. Here are a few situations 6481 * where this is known to occur, and how they may be handled by this 6482 * particular implementation: 6483 * <ul> 6484 * <li>In any mode, when the header or footer text is too wide to fit 6485 * completely in the printable area -- print as much of the text as 6486 * possible starting from the beginning, as determined by the table's 6487 * <code>ComponentOrientation</code>. 6488 * <li>In any mode, when a row is too tall to fit in the 6489 * printable area -- print the upper-most portion of the row 6490 * and paint no lower border on the table. 6491 * <li>In <code>PrintMode.NORMAL</code> when a column 6492 * is too wide to fit in the printable area -- print the center 6493 * portion of the column and leave the left and right borders 6494 * off the table. 6495 * </ul> 6496 * <p> 6497 * It is entirely valid for this <code>Printable</code> to be wrapped 6498 * inside another in order to create complex reports and documents. You may 6499 * even request that different pages be rendered into different sized 6500 * printable areas. The implementation must be prepared to handle this 6501 * (possibly by doing its layout calculations on the fly). However, 6502 * providing different heights to each page will likely not work well 6503 * with <code>PrintMode.NORMAL</code> when it has to spread columns 6504 * across pages. 6505 * <p> 6506 * As far as customizing how the table looks in the printed result, 6507 * <code>JTable</code> itself will take care of hiding the selection 6508 * and focus during printing. For additional customizations, your 6509 * renderers or painting code can customize the look based on the value 6510 * of {@link javax.swing.JComponent#isPaintingForPrint()} 6511 * <p> 6512 * Also, <i>before</i> calling this method you may wish to <i>first</i> 6513 * modify the state of the table, such as to cancel cell editing or 6514 * have the user size the table appropriately. However, you must not 6515 * modify the state of the table <i>after</i> this <code>Printable</code> 6516 * has been fetched (invalid modifications include changes in size or 6517 * underlying data). The behavior of the returned <code>Printable</code> 6518 * is undefined once the table has been changed. 6519 * 6520 * @param printMode the printing mode that the printable should use 6521 * @param headerFormat a <code>MessageFormat</code> specifying the text to 6522 * be used in printing a header, or null for none 6523 * @param footerFormat a <code>MessageFormat</code> specifying the text to 6524 * be used in printing a footer, or null for none 6525 * @return a <code>Printable</code> for printing this JTable 6526 * @see #print(JTable.PrintMode, MessageFormat, MessageFormat, 6527 * boolean, PrintRequestAttributeSet, boolean) 6528 * @see Printable 6529 * @see PrinterJob 6530 * 6531 * @since 1.5 6532 */ 6533 public Printable getPrintable(PrintMode printMode, 6534 MessageFormat headerFormat, 6535 MessageFormat footerFormat) { 6536 6537 return new TablePrintable(this, printMode, headerFormat, footerFormat); 6538 } 6539 6540 6541 /** 6542 * A <code>Printable</code> implementation that wraps another 6543 * <code>Printable</code>, making it safe for printing on another thread. 6544 */ 6545 private class ThreadSafePrintable implements Printable { 6546 6547 /** The delegate <code>Printable</code>. */ 6548 private Printable printDelegate; 6549 6550 /** 6551 * To communicate any return value when delegating. 6552 */ 6553 private int retVal; 6554 6555 /** 6556 * To communicate any <code>Throwable</code> when delegating. 6557 */ 6558 private Throwable retThrowable; 6559 6560 /** 6561 * Construct a <code>ThreadSafePrintable</code> around the given 6562 * delegate. 6563 * 6564 * @param printDelegate the <code>Printable</code> to delegate to 6565 */ 6566 public ThreadSafePrintable(Printable printDelegate) { 6567 this.printDelegate = printDelegate; 6568 } 6569 6570 /** 6571 * Prints the specified page into the given {@link Graphics} 6572 * context, in the specified format. 6573 * <p> 6574 * Regardless of what thread this method is called on, all calls into 6575 * the delegate will be done on the event-dispatch thread. 6576 * 6577 * @param graphics the context into which the page is drawn 6578 * @param pageFormat the size and orientation of the page being drawn 6579 * @param pageIndex the zero based index of the page to be drawn 6580 * @return PAGE_EXISTS if the page is rendered successfully, or 6581 * NO_SUCH_PAGE if a non-existent page index is specified 6582 * @throws PrinterException if an error causes printing to be aborted 6583 */ 6584 public int print(final Graphics graphics, 6585 final PageFormat pageFormat, 6586 final int pageIndex) throws PrinterException { 6587 6588 // We'll use this Runnable 6589 Runnable runnable = new Runnable() { 6590 public synchronized void run() { 6591 try { 6592 // call into the delegate and save the return value 6593 retVal = printDelegate.print(graphics, pageFormat, pageIndex); 6594 } catch (Throwable throwable) { 6595 // save any Throwable to be rethrown 6596 retThrowable = throwable; 6597 } finally { 6598 // notify the caller that we're done 6599 notifyAll(); 6600 } 6601 } 6602 }; 6603 6604 synchronized(runnable) { 6605 // make sure these are initialized 6606 retVal = -1; 6607 retThrowable = null; 6608 6609 // call into the EDT 6610 SwingUtilities.invokeLater(runnable); 6611 6612 // wait for the runnable to finish 6613 while (retVal == -1 && retThrowable == null) { 6614 try { 6615 runnable.wait(); 6616 } catch (InterruptedException ie) { 6617 // short process, safe to ignore interrupts 6618 } 6619 } 6620 6621 // if the delegate threw a throwable, rethrow it here 6622 if (retThrowable != null) { 6623 if (retThrowable instanceof PrinterException) { 6624 throw (PrinterException)retThrowable; 6625 } else if (retThrowable instanceof RuntimeException) { 6626 throw (RuntimeException)retThrowable; 6627 } else if (retThrowable instanceof Error) { 6628 throw (Error)retThrowable; 6629 } 6630 6631 // can not happen 6632 throw new AssertionError(retThrowable); 6633 } 6634 6635 return retVal; 6636 } 6637 } 6638 } 6639 6640 ///////////////// 6641 // Accessibility support 6642 //////////////// 6643 6644 /** 6645 * Gets the AccessibleContext associated with this JTable. 6646 * For tables, the AccessibleContext takes the form of an 6647 * AccessibleJTable. 6648 * A new AccessibleJTable instance is created if necessary. 6649 * 6650 * @return an AccessibleJTable that serves as the 6651 * AccessibleContext of this JTable 6652 */ 6653 public AccessibleContext getAccessibleContext() { 6654 if (accessibleContext == null) { 6655 accessibleContext = new AccessibleJTable(); 6656 } 6657 return accessibleContext; 6658 } 6659 6660 // 6661 // *** should also implement AccessibleSelection? 6662 // *** and what's up with keyboard navigation/manipulation? 6663 // 6664 /** 6665 * This class implements accessibility support for the 6666 * <code>JTable</code> class. It provides an implementation of the 6667 * Java Accessibility API appropriate to table user-interface elements. 6668 * <p> 6669 * <strong>Warning:</strong> 6670 * Serialized objects of this class will not be compatible with 6671 * future Swing releases. The current serialization support is 6672 * appropriate for short term storage or RMI between applications running 6673 * the same version of Swing. As of 1.4, support for long term storage 6674 * of all JavaBeans™ 6675 * has been added to the <code>java.beans</code> package. 6676 * Please see {@link java.beans.XMLEncoder}. 6677 */ 6678 @SuppressWarnings("serial") // Same-version serialization only 6679 protected class AccessibleJTable extends AccessibleJComponent 6680 implements AccessibleSelection, ListSelectionListener, TableModelListener, 6681 TableColumnModelListener, CellEditorListener, PropertyChangeListener, 6682 AccessibleExtendedTable { 6683 6684 int previousFocusedRow; 6685 int previousFocusedCol; 6686 6687 /** 6688 * AccessibleJTable constructor 6689 * 6690 * @since 1.5 6691 */ 6692 protected AccessibleJTable() { 6693 super(); 6694 JTable.this.addPropertyChangeListener(this); 6695 JTable.this.getSelectionModel().addListSelectionListener(this); 6696 TableColumnModel tcm = JTable.this.getColumnModel(); 6697 tcm.addColumnModelListener(this); 6698 tcm.getSelectionModel().addListSelectionListener(this); 6699 JTable.this.getModel().addTableModelListener(this); 6700 previousFocusedRow = JTable.this.getSelectionModel(). 6701 getLeadSelectionIndex(); 6702 previousFocusedCol = JTable.this.getColumnModel(). 6703 getSelectionModel().getLeadSelectionIndex(); 6704 } 6705 6706 // Listeners to track model, etc. changes to as to re-place the other 6707 // listeners 6708 6709 /** 6710 * Track changes to selection model, column model, etc. so as to 6711 * be able to re-place listeners on those in order to pass on 6712 * information to the Accessibility PropertyChange mechanism 6713 */ 6714 public void propertyChange(PropertyChangeEvent e) { 6715 String name = e.getPropertyName(); 6716 Object oldValue = e.getOldValue(); 6717 Object newValue = e.getNewValue(); 6718 6719 // re-set tableModel listeners 6720 if (name.compareTo("model") == 0) { 6721 6722 if (oldValue != null && oldValue instanceof TableModel) { 6723 ((TableModel) oldValue).removeTableModelListener(this); 6724 } 6725 if (newValue != null && newValue instanceof TableModel) { 6726 ((TableModel) newValue).addTableModelListener(this); 6727 } 6728 6729 // re-set selectionModel listeners 6730 } else if (name.compareTo("selectionModel") == 0) { 6731 6732 Object source = e.getSource(); 6733 if (source == JTable.this) { // row selection model 6734 6735 if (oldValue != null && 6736 oldValue instanceof ListSelectionModel) { 6737 ((ListSelectionModel) oldValue).removeListSelectionListener(this); 6738 } 6739 if (newValue != null && 6740 newValue instanceof ListSelectionModel) { 6741 ((ListSelectionModel) newValue).addListSelectionListener(this); 6742 } 6743 6744 } else if (source == JTable.this.getColumnModel()) { 6745 6746 if (oldValue != null && 6747 oldValue instanceof ListSelectionModel) { 6748 ((ListSelectionModel) oldValue).removeListSelectionListener(this); 6749 } 6750 if (newValue != null && 6751 newValue instanceof ListSelectionModel) { 6752 ((ListSelectionModel) newValue).addListSelectionListener(this); 6753 } 6754 6755 } else { 6756 // System.out.println("!!! Bug in source of selectionModel propertyChangeEvent"); 6757 } 6758 6759 // re-set columnModel listeners 6760 // and column's selection property listener as well 6761 } else if (name.compareTo("columnModel") == 0) { 6762 6763 if (oldValue != null && oldValue instanceof TableColumnModel) { 6764 TableColumnModel tcm = (TableColumnModel) oldValue; 6765 tcm.removeColumnModelListener(this); 6766 tcm.getSelectionModel().removeListSelectionListener(this); 6767 } 6768 if (newValue != null && newValue instanceof TableColumnModel) { 6769 TableColumnModel tcm = (TableColumnModel) newValue; 6770 tcm.addColumnModelListener(this); 6771 tcm.getSelectionModel().addListSelectionListener(this); 6772 } 6773 6774 // re-se cellEditor listeners 6775 } else if (name.compareTo("tableCellEditor") == 0) { 6776 6777 if (oldValue != null && oldValue instanceof TableCellEditor) { 6778 ((TableCellEditor) oldValue).removeCellEditorListener(this); 6779 } 6780 if (newValue != null && newValue instanceof TableCellEditor) { 6781 ((TableCellEditor) newValue).addCellEditorListener(this); 6782 } 6783 } 6784 } 6785 6786 6787 // Listeners to echo changes to the AccessiblePropertyChange mechanism 6788 6789 /** 6790 * Describes a change in the accessible table model. 6791 */ 6792 protected class AccessibleJTableModelChange 6793 implements AccessibleTableModelChange { 6794 6795 protected int type; 6796 protected int firstRow; 6797 protected int lastRow; 6798 protected int firstColumn; 6799 protected int lastColumn; 6800 6801 protected AccessibleJTableModelChange(int type, int firstRow, 6802 int lastRow, int firstColumn, 6803 int lastColumn) { 6804 this.type = type; 6805 this.firstRow = firstRow; 6806 this.lastRow = lastRow; 6807 this.firstColumn = firstColumn; 6808 this.lastColumn = lastColumn; 6809 } 6810 6811 public int getType() { 6812 return type; 6813 } 6814 6815 public int getFirstRow() { 6816 return firstRow; 6817 } 6818 6819 public int getLastRow() { 6820 return lastRow; 6821 } 6822 6823 public int getFirstColumn() { 6824 return firstColumn; 6825 } 6826 6827 public int getLastColumn() { 6828 return lastColumn; 6829 } 6830 } 6831 6832 /** 6833 * Track changes to the table contents 6834 * 6835 * @param e a {@code TableModelEvent} describing the event 6836 */ 6837 public void tableChanged(TableModelEvent e) { 6838 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 6839 null, null); 6840 if (e != null) { 6841 int firstColumn = e.getColumn(); 6842 int lastColumn = e.getColumn(); 6843 if (firstColumn == TableModelEvent.ALL_COLUMNS) { 6844 firstColumn = 0; 6845 lastColumn = getColumnCount() - 1; 6846 } 6847 6848 // Fire a property change event indicating the table model 6849 // has changed. 6850 AccessibleJTableModelChange change = 6851 new AccessibleJTableModelChange(e.getType(), 6852 e.getFirstRow(), 6853 e.getLastRow(), 6854 firstColumn, 6855 lastColumn); 6856 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED, 6857 null, change); 6858 } 6859 } 6860 6861 /** 6862 * Track changes to the table contents (row insertions) 6863 * 6864 * @param e a {@code TableModelEvent} describing the event 6865 */ 6866 public void tableRowsInserted(TableModelEvent e) { 6867 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 6868 null, null); 6869 6870 // Fire a property change event indicating the table model 6871 // has changed. 6872 int firstColumn = e.getColumn(); 6873 int lastColumn = e.getColumn(); 6874 if (firstColumn == TableModelEvent.ALL_COLUMNS) { 6875 firstColumn = 0; 6876 lastColumn = getColumnCount() - 1; 6877 } 6878 AccessibleJTableModelChange change = 6879 new AccessibleJTableModelChange(e.getType(), 6880 e.getFirstRow(), 6881 e.getLastRow(), 6882 firstColumn, 6883 lastColumn); 6884 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED, 6885 null, change); 6886 } 6887 6888 /** 6889 * Track changes to the table contents (row deletions) 6890 * 6891 * @param e a {@code TableModelEvent} describing the event 6892 */ 6893 public void tableRowsDeleted(TableModelEvent e) { 6894 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 6895 null, null); 6896 6897 // Fire a property change event indicating the table model 6898 // has changed. 6899 int firstColumn = e.getColumn(); 6900 int lastColumn = e.getColumn(); 6901 if (firstColumn == TableModelEvent.ALL_COLUMNS) { 6902 firstColumn = 0; 6903 lastColumn = getColumnCount() - 1; 6904 } 6905 AccessibleJTableModelChange change = 6906 new AccessibleJTableModelChange(e.getType(), 6907 e.getFirstRow(), 6908 e.getLastRow(), 6909 firstColumn, 6910 lastColumn); 6911 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED, 6912 null, change); 6913 } 6914 6915 /** 6916 * Track changes to the table contents (column insertions) 6917 */ 6918 public void columnAdded(TableColumnModelEvent e) { 6919 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 6920 null, null); 6921 6922 // Fire a property change event indicating the table model 6923 // has changed. 6924 int type = AccessibleTableModelChange.INSERT; 6925 AccessibleJTableModelChange change = 6926 new AccessibleJTableModelChange(type, 6927 0, 6928 0, 6929 e.getFromIndex(), 6930 e.getToIndex()); 6931 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED, 6932 null, change); 6933 } 6934 6935 /** 6936 * Track changes to the table contents (column deletions) 6937 */ 6938 public void columnRemoved(TableColumnModelEvent e) { 6939 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 6940 null, null); 6941 // Fire a property change event indicating the table model 6942 // has changed. 6943 int type = AccessibleTableModelChange.DELETE; 6944 AccessibleJTableModelChange change = 6945 new AccessibleJTableModelChange(type, 6946 0, 6947 0, 6948 e.getFromIndex(), 6949 e.getToIndex()); 6950 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED, 6951 null, change); 6952 } 6953 6954 /** 6955 * Track changes of a column repositioning. 6956 * 6957 * @see TableColumnModelListener 6958 */ 6959 public void columnMoved(TableColumnModelEvent e) { 6960 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 6961 null, null); 6962 6963 // Fire property change events indicating the table model 6964 // has changed. 6965 int type = AccessibleTableModelChange.DELETE; 6966 AccessibleJTableModelChange change = 6967 new AccessibleJTableModelChange(type, 6968 0, 6969 0, 6970 e.getFromIndex(), 6971 e.getFromIndex()); 6972 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED, 6973 null, change); 6974 6975 int type2 = AccessibleTableModelChange.INSERT; 6976 AccessibleJTableModelChange change2 = 6977 new AccessibleJTableModelChange(type2, 6978 0, 6979 0, 6980 e.getToIndex(), 6981 e.getToIndex()); 6982 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_MODEL_CHANGED, 6983 null, change2); 6984 } 6985 6986 /** 6987 * Track changes of a column moving due to margin changes. 6988 * 6989 * @see TableColumnModelListener 6990 */ 6991 public void columnMarginChanged(ChangeEvent e) { 6992 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 6993 null, null); 6994 } 6995 6996 /** 6997 * Track that the selection model of the TableColumnModel changed. 6998 * 6999 * @see TableColumnModelListener 7000 */ 7001 public void columnSelectionChanged(ListSelectionEvent e) { 7002 // we should now re-place our TableColumn listener 7003 } 7004 7005 /** 7006 * Track changes to a cell's contents. 7007 * 7008 * Invoked when editing is finished. The changes are saved, the 7009 * editor object is discarded, and the cell is rendered once again. 7010 * 7011 * @see CellEditorListener 7012 */ 7013 public void editingStopped(ChangeEvent e) { 7014 // it'd be great if we could figure out which cell, and pass that 7015 // somehow as a parameter 7016 firePropertyChange(AccessibleContext.ACCESSIBLE_VISIBLE_DATA_PROPERTY, 7017 null, null); 7018 } 7019 7020 /** 7021 * Invoked when editing is canceled. The editor object is discarded 7022 * and the cell is rendered once again. 7023 * 7024 * @see CellEditorListener 7025 */ 7026 public void editingCanceled(ChangeEvent e) { 7027 // nothing to report, 'cause nothing changed 7028 } 7029 7030 /** 7031 * Track changes to table cell selections 7032 */ 7033 public void valueChanged(ListSelectionEvent e) { 7034 firePropertyChange(AccessibleContext.ACCESSIBLE_SELECTION_PROPERTY, 7035 Boolean.valueOf(false), Boolean.valueOf(true)); 7036 7037 // Using lead selection index to cover both cases: node selected and node 7038 // is focused but not selected (Ctrl+up/down) 7039 int focusedRow = JTable.this.getSelectionModel().getLeadSelectionIndex(); 7040 int focusedCol = JTable.this.getColumnModel().getSelectionModel(). 7041 getLeadSelectionIndex(); 7042 7043 if (focusedRow != previousFocusedRow || 7044 focusedCol != previousFocusedCol) { 7045 Accessible oldA = getAccessibleAt(previousFocusedRow, previousFocusedCol); 7046 Accessible newA = getAccessibleAt(focusedRow, focusedCol); 7047 firePropertyChange(AccessibleContext.ACCESSIBLE_ACTIVE_DESCENDANT_PROPERTY, 7048 oldA, newA); 7049 previousFocusedRow = focusedRow; 7050 previousFocusedCol = focusedCol; 7051 } 7052 } 7053 7054 7055 7056 7057 // AccessibleContext support 7058 7059 /** 7060 * Get the AccessibleSelection associated with this object. In the 7061 * implementation of the Java Accessibility API for this class, 7062 * return this object, which is responsible for implementing the 7063 * AccessibleSelection interface on behalf of itself. 7064 * 7065 * @return this object 7066 */ 7067 public AccessibleSelection getAccessibleSelection() { 7068 return this; 7069 } 7070 7071 /** 7072 * Gets the role of this object. 7073 * 7074 * @return an instance of AccessibleRole describing the role of the 7075 * object 7076 * @see AccessibleRole 7077 */ 7078 public AccessibleRole getAccessibleRole() { 7079 return AccessibleRole.TABLE; 7080 } 7081 7082 /** 7083 * Returns the <code>Accessible</code> child, if one exists, 7084 * contained at the local coordinate <code>Point</code>. 7085 * 7086 * @param p the point defining the top-left corner of the 7087 * <code>Accessible</code>, given in the coordinate space 7088 * of the object's parent 7089 * @return the <code>Accessible</code>, if it exists, 7090 * at the specified location; else <code>null</code> 7091 */ 7092 public Accessible getAccessibleAt(Point p) { 7093 int column = columnAtPoint(p); 7094 int row = rowAtPoint(p); 7095 7096 if ((column != -1) && (row != -1)) { 7097 TableColumn aColumn = getColumnModel().getColumn(column); 7098 TableCellRenderer renderer = aColumn.getCellRenderer(); 7099 if (renderer == null) { 7100 Class<?> columnClass = getColumnClass(column); 7101 renderer = getDefaultRenderer(columnClass); 7102 } 7103 Component component = renderer.getTableCellRendererComponent( 7104 JTable.this, null, false, false, 7105 row, column); 7106 return new AccessibleJTableCell(JTable.this, row, column, 7107 getAccessibleIndexAt(row, column)); 7108 } 7109 return null; 7110 } 7111 7112 /** 7113 * Returns the number of accessible children in the object. If all 7114 * of the children of this object implement <code>Accessible</code>, 7115 * then this method should return the number of children of this object. 7116 * 7117 * @return the number of accessible children in the object 7118 */ 7119 public int getAccessibleChildrenCount() { 7120 return (JTable.this.getColumnCount() * JTable.this.getRowCount()); 7121 } 7122 7123 /** 7124 * Returns the nth <code>Accessible</code> child of the object. 7125 * 7126 * @param i zero-based index of child 7127 * @return the nth Accessible child of the object 7128 */ 7129 public Accessible getAccessibleChild(int i) { 7130 if (i < 0 || i >= getAccessibleChildrenCount()) { 7131 return null; 7132 } else { 7133 // children increase across, and then down, for tables 7134 // (arbitrary decision) 7135 int column = getAccessibleColumnAtIndex(i); 7136 int row = getAccessibleRowAtIndex(i); 7137 7138 TableColumn aColumn = getColumnModel().getColumn(column); 7139 TableCellRenderer renderer = aColumn.getCellRenderer(); 7140 if (renderer == null) { 7141 Class<?> columnClass = getColumnClass(column); 7142 renderer = getDefaultRenderer(columnClass); 7143 } 7144 Component component = renderer.getTableCellRendererComponent( 7145 JTable.this, null, false, false, 7146 row, column); 7147 return new AccessibleJTableCell(JTable.this, row, column, 7148 getAccessibleIndexAt(row, column)); 7149 } 7150 } 7151 7152 // AccessibleSelection support 7153 7154 /** 7155 * Returns the number of <code>Accessible</code> children 7156 * currently selected. 7157 * If no children are selected, the return value will be 0. 7158 * 7159 * @return the number of items currently selected 7160 */ 7161 public int getAccessibleSelectionCount() { 7162 int rowsSel = JTable.this.getSelectedRowCount(); 7163 int colsSel = JTable.this.getSelectedColumnCount(); 7164 7165 if (JTable.this.cellSelectionEnabled) { // a contiguous block 7166 return rowsSel * colsSel; 7167 7168 } else { 7169 // a column swath and a row swath, with a shared block 7170 if (JTable.this.getRowSelectionAllowed() && 7171 JTable.this.getColumnSelectionAllowed()) { 7172 return rowsSel * JTable.this.getColumnCount() + 7173 colsSel * JTable.this.getRowCount() - 7174 rowsSel * colsSel; 7175 7176 // just one or more rows in selection 7177 } else if (JTable.this.getRowSelectionAllowed()) { 7178 return rowsSel * JTable.this.getColumnCount(); 7179 7180 // just one or more rows in selection 7181 } else if (JTable.this.getColumnSelectionAllowed()) { 7182 return colsSel * JTable.this.getRowCount(); 7183 7184 } else { 7185 return 0; // JTable doesn't allow selections 7186 } 7187 } 7188 } 7189 7190 /** 7191 * Returns an <code>Accessible</code> representing the 7192 * specified selected child in the object. If there 7193 * isn't a selection, or there are fewer children selected 7194 * than the integer passed in, the return 7195 * value will be <code>null</code>. 7196 * <p>Note that the index represents the i-th selected child, which 7197 * is different from the i-th child. 7198 * 7199 * @param i the zero-based index of selected children 7200 * @return the i-th selected child 7201 * @see #getAccessibleSelectionCount 7202 */ 7203 public Accessible getAccessibleSelection(int i) { 7204 if (i < 0 || i > getAccessibleSelectionCount()) { 7205 return null; 7206 } 7207 7208 int rowsSel = JTable.this.getSelectedRowCount(); 7209 int colsSel = JTable.this.getSelectedColumnCount(); 7210 int rowIndicies[] = getSelectedRows(); 7211 int colIndicies[] = getSelectedColumns(); 7212 int ttlCols = JTable.this.getColumnCount(); 7213 int ttlRows = JTable.this.getRowCount(); 7214 int r; 7215 int c; 7216 7217 if (JTable.this.cellSelectionEnabled) { // a contiguous block 7218 r = rowIndicies[i / colsSel]; 7219 c = colIndicies[i % colsSel]; 7220 return getAccessibleChild((r * ttlCols) + c); 7221 } else { 7222 7223 // a column swath and a row swath, with a shared block 7224 if (JTable.this.getRowSelectionAllowed() && 7225 JTable.this.getColumnSelectionAllowed()) { 7226 7227 // Situation: 7228 // We have a table, like the 6x3 table below, 7229 // wherein three colums and one row selected 7230 // (selected cells marked with "*", unselected "0"): 7231 // 7232 // 0 * 0 * * 0 7233 // * * * * * * 7234 // 0 * 0 * * 0 7235 // 7236 7237 // State machine below walks through the array of 7238 // selected rows in two states: in a selected row, 7239 // and not in one; continuing until we are in a row 7240 // in which the ith selection exists. Then we return 7241 // the appropriate cell. In the state machine, we 7242 // always do rows above the "current" selected row first, 7243 // then the cells in the selected row. If we're done 7244 // with the state machine before finding the requested 7245 // selected child, we handle the rows below the last 7246 // selected row at the end. 7247 // 7248 int curIndex = i; 7249 final int IN_ROW = 0; 7250 final int NOT_IN_ROW = 1; 7251 int state = (rowIndicies[0] == 0 ? IN_ROW : NOT_IN_ROW); 7252 int j = 0; 7253 int prevRow = -1; 7254 while (j < rowIndicies.length) { 7255 switch (state) { 7256 7257 case IN_ROW: // on individual row full of selections 7258 if (curIndex < ttlCols) { // it's here! 7259 c = curIndex % ttlCols; 7260 r = rowIndicies[j]; 7261 return getAccessibleChild((r * ttlCols) + c); 7262 } else { // not here 7263 curIndex -= ttlCols; 7264 } 7265 // is the next row in table selected or not? 7266 if (j + 1 == rowIndicies.length || 7267 rowIndicies[j] != rowIndicies[j+1] - 1) { 7268 state = NOT_IN_ROW; 7269 prevRow = rowIndicies[j]; 7270 } 7271 j++; // we didn't return earlier, so go to next row 7272 break; 7273 7274 case NOT_IN_ROW: // sparse bunch of rows of selections 7275 if (curIndex < 7276 (colsSel * (rowIndicies[j] - 7277 (prevRow == -1 ? 0 : (prevRow + 1))))) { 7278 7279 // it's here! 7280 c = colIndicies[curIndex % colsSel]; 7281 r = (j > 0 ? rowIndicies[j-1] + 1 : 0) 7282 + curIndex / colsSel; 7283 return getAccessibleChild((r * ttlCols) + c); 7284 } else { // not here 7285 curIndex -= colsSel * (rowIndicies[j] - 7286 (prevRow == -1 ? 0 : (prevRow + 1))); 7287 } 7288 state = IN_ROW; 7289 break; 7290 } 7291 } 7292 // we got here, so we didn't find it yet; find it in 7293 // the last sparse bunch of rows 7294 if (curIndex < 7295 (colsSel * (ttlRows - 7296 (prevRow == -1 ? 0 : (prevRow + 1))))) { // it's here! 7297 c = colIndicies[curIndex % colsSel]; 7298 r = rowIndicies[j-1] + curIndex / colsSel + 1; 7299 return getAccessibleChild((r * ttlCols) + c); 7300 } else { // not here 7301 // we shouldn't get to this spot in the code! 7302 // System.out.println("Bug in AccessibleJTable.getAccessibleSelection()"); 7303 } 7304 7305 // one or more rows selected 7306 } else if (JTable.this.getRowSelectionAllowed()) { 7307 c = i % ttlCols; 7308 r = rowIndicies[i / ttlCols]; 7309 return getAccessibleChild((r * ttlCols) + c); 7310 7311 // one or more columns selected 7312 } else if (JTable.this.getColumnSelectionAllowed()) { 7313 c = colIndicies[i % colsSel]; 7314 r = i / colsSel; 7315 return getAccessibleChild((r * ttlCols) + c); 7316 } 7317 } 7318 return null; 7319 } 7320 7321 /** 7322 * Determines if the current child of this object is selected. 7323 * 7324 * @param i the zero-based index of the child in this 7325 * <code>Accessible</code> object 7326 * @return true if the current child of this object is selected 7327 * @see AccessibleContext#getAccessibleChild 7328 */ 7329 public boolean isAccessibleChildSelected(int i) { 7330 int column = getAccessibleColumnAtIndex(i); 7331 int row = getAccessibleRowAtIndex(i); 7332 return JTable.this.isCellSelected(row, column); 7333 } 7334 7335 /** 7336 * Adds the specified <code>Accessible</code> child of the 7337 * object to the object's selection. If the object supports 7338 * multiple selections, the specified child is added to 7339 * any existing selection, otherwise 7340 * it replaces any existing selection in the object. If the 7341 * specified child is already selected, this method has no effect. 7342 * <p> 7343 * This method only works on <code>JTable</code>s which have 7344 * individual cell selection enabled. 7345 * 7346 * @param i the zero-based index of the child 7347 * @see AccessibleContext#getAccessibleChild 7348 */ 7349 public void addAccessibleSelection(int i) { 7350 // TIGER - 4495286 7351 int column = getAccessibleColumnAtIndex(i); 7352 int row = getAccessibleRowAtIndex(i); 7353 JTable.this.changeSelection(row, column, true, false); 7354 } 7355 7356 /** 7357 * Removes the specified child of the object from the object's 7358 * selection. If the specified item isn't currently selected, this 7359 * method has no effect. 7360 * <p> 7361 * This method only works on <code>JTables</code> which have 7362 * individual cell selection enabled. 7363 * 7364 * @param i the zero-based index of the child 7365 * @see AccessibleContext#getAccessibleChild 7366 */ 7367 public void removeAccessibleSelection(int i) { 7368 if (JTable.this.cellSelectionEnabled) { 7369 int column = getAccessibleColumnAtIndex(i); 7370 int row = getAccessibleRowAtIndex(i); 7371 JTable.this.removeRowSelectionInterval(row, row); 7372 JTable.this.removeColumnSelectionInterval(column, column); 7373 } 7374 } 7375 7376 /** 7377 * Clears the selection in the object, so that no children in the 7378 * object are selected. 7379 */ 7380 public void clearAccessibleSelection() { 7381 JTable.this.clearSelection(); 7382 } 7383 7384 /** 7385 * Causes every child of the object to be selected, but only 7386 * if the <code>JTable</code> supports multiple selections, 7387 * and if individual cell selection is enabled. 7388 */ 7389 public void selectAllAccessibleSelection() { 7390 if (JTable.this.cellSelectionEnabled) { 7391 JTable.this.selectAll(); 7392 } 7393 } 7394 7395 // begin AccessibleExtendedTable implementation ------------- 7396 7397 /** 7398 * Returns the row number of an index in the table. 7399 * 7400 * @param index the zero-based index in the table 7401 * @return the zero-based row of the table if one exists; 7402 * otherwise -1. 7403 * @since 1.4 7404 */ 7405 public int getAccessibleRow(int index) { 7406 return getAccessibleRowAtIndex(index); 7407 } 7408 7409 /** 7410 * Returns the column number of an index in the table. 7411 * 7412 * @param index the zero-based index in the table 7413 * @return the zero-based column of the table if one exists; 7414 * otherwise -1. 7415 * @since 1.4 7416 */ 7417 public int getAccessibleColumn(int index) { 7418 return getAccessibleColumnAtIndex(index); 7419 } 7420 7421 /** 7422 * Returns the index at a row and column in the table. 7423 * 7424 * @param r zero-based row of the table 7425 * @param c zero-based column of the table 7426 * @return the zero-based index in the table if one exists; 7427 * otherwise -1. 7428 * @since 1.4 7429 */ 7430 public int getAccessibleIndex(int r, int c) { 7431 return getAccessibleIndexAt(r, c); 7432 } 7433 7434 // end of AccessibleExtendedTable implementation ------------ 7435 7436 // start of AccessibleTable implementation ------------------ 7437 7438 private Accessible caption; 7439 private Accessible summary; 7440 private Accessible [] rowDescription; 7441 private Accessible [] columnDescription; 7442 7443 /** 7444 * Gets the <code>AccessibleTable</code> associated with this 7445 * object. In the implementation of the Java Accessibility 7446 * API for this class, return this object, which is responsible 7447 * for implementing the <code>AccessibleTables</code> interface 7448 * on behalf of itself. 7449 * 7450 * @return this object 7451 * @since 1.3 7452 */ 7453 public AccessibleTable getAccessibleTable() { 7454 return this; 7455 } 7456 7457 /** 7458 * Returns the caption for the table. 7459 * 7460 * @return the caption for the table 7461 * @since 1.3 7462 */ 7463 public Accessible getAccessibleCaption() { 7464 return this.caption; 7465 } 7466 7467 /** 7468 * Sets the caption for the table. 7469 * 7470 * @param a the caption for the table 7471 * @since 1.3 7472 */ 7473 public void setAccessibleCaption(Accessible a) { 7474 Accessible oldCaption = caption; 7475 this.caption = a; 7476 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_CAPTION_CHANGED, 7477 oldCaption, this.caption); 7478 } 7479 7480 /** 7481 * Returns the summary description of the table. 7482 * 7483 * @return the summary description of the table 7484 * @since 1.3 7485 */ 7486 public Accessible getAccessibleSummary() { 7487 return this.summary; 7488 } 7489 7490 /** 7491 * Sets the summary description of the table. 7492 * 7493 * @param a the summary description of the table 7494 * @since 1.3 7495 */ 7496 public void setAccessibleSummary(Accessible a) { 7497 Accessible oldSummary = summary; 7498 this.summary = a; 7499 firePropertyChange(AccessibleContext.ACCESSIBLE_TABLE_SUMMARY_CHANGED, 7500 oldSummary, this.summary); 7501 } 7502 7503 /* 7504 * Returns the total number of rows in this table. 7505 * 7506 * @return the total number of rows in this table 7507 */ 7508 public int getAccessibleRowCount() { 7509 return JTable.this.getRowCount(); 7510 } 7511 7512 /* 7513 * Returns the total number of columns in the table. 7514 * 7515 * @return the total number of columns in the table 7516 */ 7517 public int getAccessibleColumnCount() { 7518 return JTable.this.getColumnCount(); 7519 } 7520 7521 /* 7522 * Returns the <code>Accessible</code> at a specified row 7523 * and column in the table. 7524 * 7525 * @param r zero-based row of the table 7526 * @param c zero-based column of the table 7527 * @return the <code>Accessible</code> at the specified row and column 7528 * in the table 7529 */ 7530 public Accessible getAccessibleAt(int r, int c) { 7531 return getAccessibleChild((r * getAccessibleColumnCount()) + c); 7532 } 7533 7534 /** 7535 * Returns the number of rows occupied by the <code>Accessible</code> 7536 * at a specified row and column in the table. 7537 * 7538 * @return the number of rows occupied by the <code>Accessible</code> 7539 * at a specified row and column in the table 7540 * @since 1.3 7541 */ 7542 public int getAccessibleRowExtentAt(int r, int c) { 7543 return 1; 7544 } 7545 7546 /** 7547 * Returns the number of columns occupied by the 7548 * <code>Accessible</code> at a given (row, column). 7549 * 7550 * @return the number of columns occupied by the <code>Accessible</code> 7551 * at a specified row and column in the table 7552 * @since 1.3 7553 */ 7554 public int getAccessibleColumnExtentAt(int r, int c) { 7555 return 1; 7556 } 7557 7558 /** 7559 * Returns the row headers as an <code>AccessibleTable</code>. 7560 * 7561 * @return an <code>AccessibleTable</code> representing the row 7562 * headers 7563 * @since 1.3 7564 */ 7565 public AccessibleTable getAccessibleRowHeader() { 7566 // row headers are not supported 7567 return null; 7568 } 7569 7570 /** 7571 * Sets the row headers as an <code>AccessibleTable</code>. 7572 * 7573 * @param a an <code>AccessibleTable</code> representing the row 7574 * headers 7575 * @since 1.3 7576 */ 7577 public void setAccessibleRowHeader(AccessibleTable a) { 7578 // row headers are not supported 7579 } 7580 7581 /** 7582 * Returns the column headers as an <code>AccessibleTable</code>. 7583 * 7584 * @return an <code>AccessibleTable</code> representing the column 7585 * headers, or <code>null</code> if the table header is 7586 * <code>null</code> 7587 * @since 1.3 7588 */ 7589 public AccessibleTable getAccessibleColumnHeader() { 7590 JTableHeader header = JTable.this.getTableHeader(); 7591 return header == null ? null : new AccessibleTableHeader(header); 7592 } 7593 7594 /* 7595 * Private class representing a table column header 7596 */ 7597 private class AccessibleTableHeader implements AccessibleTable { 7598 private JTableHeader header; 7599 private TableColumnModel headerModel; 7600 7601 AccessibleTableHeader(JTableHeader header) { 7602 this.header = header; 7603 this.headerModel = header.getColumnModel(); 7604 } 7605 7606 /** 7607 * Returns the caption for the table. 7608 * 7609 * @return the caption for the table 7610 */ 7611 public Accessible getAccessibleCaption() { return null; } 7612 7613 7614 /** 7615 * Sets the caption for the table. 7616 * 7617 * @param a the caption for the table 7618 */ 7619 public void setAccessibleCaption(Accessible a) {} 7620 7621 /** 7622 * Returns the summary description of the table. 7623 * 7624 * @return the summary description of the table 7625 */ 7626 public Accessible getAccessibleSummary() { return null; } 7627 7628 /** 7629 * Sets the summary description of the table 7630 * 7631 * @param a the summary description of the table 7632 */ 7633 public void setAccessibleSummary(Accessible a) {} 7634 7635 /** 7636 * Returns the number of rows in the table. 7637 * 7638 * @return the number of rows in the table 7639 */ 7640 public int getAccessibleRowCount() { return 1; } 7641 7642 /** 7643 * Returns the number of columns in the table. 7644 * 7645 * @return the number of columns in the table 7646 */ 7647 public int getAccessibleColumnCount() { 7648 return headerModel.getColumnCount(); 7649 } 7650 7651 /** 7652 * Returns the Accessible at a specified row and column 7653 * in the table. 7654 * 7655 * @param row zero-based row of the table 7656 * @param column zero-based column of the table 7657 * @return the Accessible at the specified row and column 7658 */ 7659 public Accessible getAccessibleAt(int row, int column) { 7660 7661 7662 // TIGER - 4715503 7663 TableColumn aColumn = headerModel.getColumn(column); 7664 TableCellRenderer renderer = aColumn.getHeaderRenderer(); 7665 if (renderer == null) { 7666 renderer = header.getDefaultRenderer(); 7667 } 7668 Component component = renderer.getTableCellRendererComponent( 7669 header.getTable(), 7670 aColumn.getHeaderValue(), false, false, 7671 -1, column); 7672 7673 return new AccessibleJTableHeaderCell(row, column, 7674 JTable.this.getTableHeader(), 7675 component); 7676 } 7677 7678 /** 7679 * Returns the number of rows occupied by the Accessible at 7680 * a specified row and column in the table. 7681 * 7682 * @return the number of rows occupied by the Accessible at a 7683 * given specified (row, column) 7684 */ 7685 public int getAccessibleRowExtentAt(int r, int c) { return 1; } 7686 7687 /** 7688 * Returns the number of columns occupied by the Accessible at 7689 * a specified row and column in the table. 7690 * 7691 * @return the number of columns occupied by the Accessible at a 7692 * given specified row and column 7693 */ 7694 public int getAccessibleColumnExtentAt(int r, int c) { return 1; } 7695 7696 /** 7697 * Returns the row headers as an AccessibleTable. 7698 * 7699 * @return an AccessibleTable representing the row 7700 * headers 7701 */ 7702 public AccessibleTable getAccessibleRowHeader() { return null; } 7703 7704 /** 7705 * Sets the row headers. 7706 * 7707 * @param table an AccessibleTable representing the 7708 * row headers 7709 */ 7710 public void setAccessibleRowHeader(AccessibleTable table) {} 7711 7712 /** 7713 * Returns the column headers as an AccessibleTable. 7714 * 7715 * @return an AccessibleTable representing the column 7716 * headers 7717 */ 7718 public AccessibleTable getAccessibleColumnHeader() { return null; } 7719 7720 /** 7721 * Sets the column headers. 7722 * 7723 * @param table an AccessibleTable representing the 7724 * column headers 7725 * @since 1.3 7726 */ 7727 public void setAccessibleColumnHeader(AccessibleTable table) {} 7728 7729 /** 7730 * Returns the description of the specified row in the table. 7731 * 7732 * @param r zero-based row of the table 7733 * @return the description of the row 7734 * @since 1.3 7735 */ 7736 public Accessible getAccessibleRowDescription(int r) { return null; } 7737 7738 /** 7739 * Sets the description text of the specified row of the table. 7740 * 7741 * @param r zero-based row of the table 7742 * @param a the description of the row 7743 * @since 1.3 7744 */ 7745 public void setAccessibleRowDescription(int r, Accessible a) {} 7746 7747 /** 7748 * Returns the description text of the specified column in the table. 7749 * 7750 * @param c zero-based column of the table 7751 * @return the text description of the column 7752 * @since 1.3 7753 */ 7754 public Accessible getAccessibleColumnDescription(int c) { return null; } 7755 7756 /** 7757 * Sets the description text of the specified column in the table. 7758 * 7759 * @param c zero-based column of the table 7760 * @param a the text description of the column 7761 * @since 1.3 7762 */ 7763 public void setAccessibleColumnDescription(int c, Accessible a) {} 7764 7765 /** 7766 * Returns a boolean value indicating whether the accessible at 7767 * a specified row and column is selected. 7768 * 7769 * @param r zero-based row of the table 7770 * @param c zero-based column of the table 7771 * @return the boolean value true if the accessible at the 7772 * row and column is selected. Otherwise, the boolean value 7773 * false 7774 * @since 1.3 7775 */ 7776 public boolean isAccessibleSelected(int r, int c) { return false; } 7777 7778 /** 7779 * Returns a boolean value indicating whether the specified row 7780 * is selected. 7781 * 7782 * @param r zero-based row of the table 7783 * @return the boolean value true if the specified row is selected. 7784 * Otherwise, false. 7785 * @since 1.3 7786 */ 7787 public boolean isAccessibleRowSelected(int r) { return false; } 7788 7789 /** 7790 * Returns a boolean value indicating whether the specified column 7791 * is selected. 7792 * 7793 * @param c zero-based column of the table 7794 * @return the boolean value true if the specified column is selected. 7795 * Otherwise, false. 7796 * @since 1.3 7797 */ 7798 public boolean isAccessibleColumnSelected(int c) { return false; } 7799 7800 /** 7801 * Returns the selected rows in a table. 7802 * 7803 * @return an array of selected rows where each element is a 7804 * zero-based row of the table 7805 * @since 1.3 7806 */ 7807 public int [] getSelectedAccessibleRows() { return new int[0]; } 7808 7809 /** 7810 * Returns the selected columns in a table. 7811 * 7812 * @return an array of selected columns where each element is a 7813 * zero-based column of the table 7814 * @since 1.3 7815 */ 7816 public int [] getSelectedAccessibleColumns() { return new int[0]; } 7817 } 7818 7819 7820 /** 7821 * Sets the column headers as an <code>AccessibleTable</code>. 7822 * 7823 * @param a an <code>AccessibleTable</code> representing the 7824 * column headers 7825 * @since 1.3 7826 */ 7827 public void setAccessibleColumnHeader(AccessibleTable a) { 7828 // XXX not implemented 7829 } 7830 7831 /** 7832 * Returns the description of the specified row in the table. 7833 * 7834 * @param r zero-based row of the table 7835 * @return the description of the row 7836 * @since 1.3 7837 */ 7838 public Accessible getAccessibleRowDescription(int r) { 7839 if (r < 0 || r >= getAccessibleRowCount()) { 7840 throw new IllegalArgumentException(Integer.toString(r)); 7841 } 7842 if (rowDescription == null) { 7843 return null; 7844 } else { 7845 return rowDescription[r]; 7846 } 7847 } 7848 7849 /** 7850 * Sets the description text of the specified row of the table. 7851 * 7852 * @param r zero-based row of the table 7853 * @param a the description of the row 7854 * @since 1.3 7855 */ 7856 public void setAccessibleRowDescription(int r, Accessible a) { 7857 if (r < 0 || r >= getAccessibleRowCount()) { 7858 throw new IllegalArgumentException(Integer.toString(r)); 7859 } 7860 if (rowDescription == null) { 7861 int numRows = getAccessibleRowCount(); 7862 rowDescription = new Accessible[numRows]; 7863 } 7864 rowDescription[r] = a; 7865 } 7866 7867 /** 7868 * Returns the description of the specified column in the table. 7869 * 7870 * @param c zero-based column of the table 7871 * @return the description of the column 7872 * @since 1.3 7873 */ 7874 public Accessible getAccessibleColumnDescription(int c) { 7875 if (c < 0 || c >= getAccessibleColumnCount()) { 7876 throw new IllegalArgumentException(Integer.toString(c)); 7877 } 7878 if (columnDescription == null) { 7879 return null; 7880 } else { 7881 return columnDescription[c]; 7882 } 7883 } 7884 7885 /** 7886 * Sets the description text of the specified column of the table. 7887 * 7888 * @param c zero-based column of the table 7889 * @param a the description of the column 7890 * @since 1.3 7891 */ 7892 public void setAccessibleColumnDescription(int c, Accessible a) { 7893 if (c < 0 || c >= getAccessibleColumnCount()) { 7894 throw new IllegalArgumentException(Integer.toString(c)); 7895 } 7896 if (columnDescription == null) { 7897 int numColumns = getAccessibleColumnCount(); 7898 columnDescription = new Accessible[numColumns]; 7899 } 7900 columnDescription[c] = a; 7901 } 7902 7903 /** 7904 * Returns a boolean value indicating whether the accessible at a 7905 * given (row, column) is selected. 7906 * 7907 * @param r zero-based row of the table 7908 * @param c zero-based column of the table 7909 * @return the boolean value true if the accessible at (row, column) 7910 * is selected; otherwise, the boolean value false 7911 * @since 1.3 7912 */ 7913 public boolean isAccessibleSelected(int r, int c) { 7914 return JTable.this.isCellSelected(r, c); 7915 } 7916 7917 /** 7918 * Returns a boolean value indicating whether the specified row 7919 * is selected. 7920 * 7921 * @param r zero-based row of the table 7922 * @return the boolean value true if the specified row is selected; 7923 * otherwise, false 7924 * @since 1.3 7925 */ 7926 public boolean isAccessibleRowSelected(int r) { 7927 return JTable.this.isRowSelected(r); 7928 } 7929 7930 /** 7931 * Returns a boolean value indicating whether the specified column 7932 * is selected. 7933 * 7934 * @param c zero-based column of the table 7935 * @return the boolean value true if the specified column is selected; 7936 * otherwise, false 7937 * @since 1.3 7938 */ 7939 public boolean isAccessibleColumnSelected(int c) { 7940 return JTable.this.isColumnSelected(c); 7941 } 7942 7943 /** 7944 * Returns the selected rows in a table. 7945 * 7946 * @return an array of selected rows where each element is a 7947 * zero-based row of the table 7948 * @since 1.3 7949 */ 7950 public int [] getSelectedAccessibleRows() { 7951 return JTable.this.getSelectedRows(); 7952 } 7953 7954 /** 7955 * Returns the selected columns in a table. 7956 * 7957 * @return an array of selected columns where each element is a 7958 * zero-based column of the table 7959 * @since 1.3 7960 */ 7961 public int [] getSelectedAccessibleColumns() { 7962 return JTable.this.getSelectedColumns(); 7963 } 7964 7965 /** 7966 * Returns the row at a given index into the table. 7967 * 7968 * @param i zero-based index into the table 7969 * @return the row at a given index 7970 * @since 1.3 7971 */ 7972 public int getAccessibleRowAtIndex(int i) { 7973 int columnCount = getAccessibleColumnCount(); 7974 if (columnCount == 0) { 7975 return -1; 7976 } else { 7977 return (i / columnCount); 7978 } 7979 } 7980 7981 /** 7982 * Returns the column at a given index into the table. 7983 * 7984 * @param i zero-based index into the table 7985 * @return the column at a given index 7986 * @since 1.3 7987 */ 7988 public int getAccessibleColumnAtIndex(int i) { 7989 int columnCount = getAccessibleColumnCount(); 7990 if (columnCount == 0) { 7991 return -1; 7992 } else { 7993 return (i % columnCount); 7994 } 7995 } 7996 7997 /** 7998 * Returns the index at a given (row, column) in the table. 7999 * 8000 * @param r zero-based row of the table 8001 * @param c zero-based column of the table 8002 * @return the index into the table 8003 * @since 1.3 8004 */ 8005 public int getAccessibleIndexAt(int r, int c) { 8006 return ((r * getAccessibleColumnCount()) + c); 8007 } 8008 8009 // end of AccessibleTable implementation -------------------- 8010 8011 /** 8012 * The class provides an implementation of the Java Accessibility 8013 * API appropriate to table cells. 8014 */ 8015 protected class AccessibleJTableCell extends AccessibleContext 8016 implements Accessible, AccessibleComponent { 8017 8018 private JTable parent; 8019 private int row; 8020 private int column; 8021 private int index; 8022 8023 /** 8024 * Constructs an <code>AccessibleJTableHeaderEntry</code>. 8025 * 8026 * @param t a {@code JTable} 8027 * @param r an {@code int} specifying a row 8028 * @param c an {@code int} specifying a column 8029 * @param i an {@code int} specifying the index to this cell 8030 * @since 1.4 8031 */ 8032 public AccessibleJTableCell(JTable t, int r, int c, int i) { 8033 parent = t; 8034 row = r; 8035 column = c; 8036 index = i; 8037 this.setAccessibleParent(parent); 8038 } 8039 8040 /** 8041 * Gets the <code>AccessibleContext</code> associated with this 8042 * component. In the implementation of the Java Accessibility 8043 * API for this class, return this object, which is its own 8044 * <code>AccessibleContext</code>. 8045 * 8046 * @return this object 8047 */ 8048 public AccessibleContext getAccessibleContext() { 8049 return this; 8050 } 8051 8052 /** 8053 * Gets the AccessibleContext for the table cell renderer. 8054 * 8055 * @return the <code>AccessibleContext</code> for the table 8056 * cell renderer if one exists; 8057 * otherwise, returns <code>null</code>. 8058 * @since 1.6 8059 */ 8060 protected AccessibleContext getCurrentAccessibleContext() { 8061 TableColumn aColumn = getColumnModel().getColumn(column); 8062 TableCellRenderer renderer = aColumn.getCellRenderer(); 8063 if (renderer == null) { 8064 Class<?> columnClass = getColumnClass(column); 8065 renderer = getDefaultRenderer(columnClass); 8066 } 8067 Component component = renderer.getTableCellRendererComponent( 8068 JTable.this, getValueAt(row, column), 8069 false, false, row, column); 8070 if (component instanceof Accessible) { 8071 return component.getAccessibleContext(); 8072 } else { 8073 return null; 8074 } 8075 } 8076 8077 /** 8078 * Gets the table cell renderer component. 8079 * 8080 * @return the table cell renderer component if one exists; 8081 * otherwise, returns <code>null</code>. 8082 * @since 1.6 8083 */ 8084 protected Component getCurrentComponent() { 8085 TableColumn aColumn = getColumnModel().getColumn(column); 8086 TableCellRenderer renderer = aColumn.getCellRenderer(); 8087 if (renderer == null) { 8088 Class<?> columnClass = getColumnClass(column); 8089 renderer = getDefaultRenderer(columnClass); 8090 } 8091 return renderer.getTableCellRendererComponent( 8092 JTable.this, null, false, false, 8093 row, column); 8094 } 8095 8096 // AccessibleContext methods 8097 8098 /** 8099 * Gets the accessible name of this object. 8100 * 8101 * @return the localized name of the object; <code>null</code> 8102 * if this object does not have a name 8103 */ 8104 public String getAccessibleName() { 8105 AccessibleContext ac = getCurrentAccessibleContext(); 8106 if (ac != null) { 8107 String name = ac.getAccessibleName(); 8108 if ((name != null) && (name != "")) { 8109 // return the cell renderer's AccessibleName 8110 return name; 8111 } 8112 } 8113 if ((accessibleName != null) && (accessibleName != "")) { 8114 return accessibleName; 8115 } else { 8116 // fall back to the client property 8117 return (String)getClientProperty(AccessibleContext.ACCESSIBLE_NAME_PROPERTY); 8118 } 8119 } 8120 8121 /** 8122 * Sets the localized accessible name of this object. 8123 * 8124 * @param s the new localized name of the object 8125 */ 8126 public void setAccessibleName(String s) { 8127 AccessibleContext ac = getCurrentAccessibleContext(); 8128 if (ac != null) { 8129 ac.setAccessibleName(s); 8130 } else { 8131 super.setAccessibleName(s); 8132 } 8133 } 8134 8135 // 8136 // *** should check toolTip text for desc. (needs MouseEvent) 8137 // 8138 /** 8139 * Gets the accessible description of this object. 8140 * 8141 * @return the localized description of the object; 8142 * <code>null</code> if this object does not have 8143 * a description 8144 */ 8145 public String getAccessibleDescription() { 8146 AccessibleContext ac = getCurrentAccessibleContext(); 8147 if (ac != null) { 8148 return ac.getAccessibleDescription(); 8149 } else { 8150 return super.getAccessibleDescription(); 8151 } 8152 } 8153 8154 /** 8155 * Sets the accessible description of this object. 8156 * 8157 * @param s the new localized description of the object 8158 */ 8159 public void setAccessibleDescription(String s) { 8160 AccessibleContext ac = getCurrentAccessibleContext(); 8161 if (ac != null) { 8162 ac.setAccessibleDescription(s); 8163 } else { 8164 super.setAccessibleDescription(s); 8165 } 8166 } 8167 8168 /** 8169 * Gets the role of this object. 8170 * 8171 * @return an instance of <code>AccessibleRole</code> 8172 * describing the role of the object 8173 * @see AccessibleRole 8174 */ 8175 public AccessibleRole getAccessibleRole() { 8176 AccessibleContext ac = getCurrentAccessibleContext(); 8177 if (ac != null) { 8178 return ac.getAccessibleRole(); 8179 } else { 8180 return AccessibleRole.UNKNOWN; 8181 } 8182 } 8183 8184 /** 8185 * Gets the state set of this object. 8186 * 8187 * @return an instance of <code>AccessibleStateSet</code> 8188 * containing the current state set of the object 8189 * @see AccessibleState 8190 */ 8191 public AccessibleStateSet getAccessibleStateSet() { 8192 AccessibleContext ac = getCurrentAccessibleContext(); 8193 AccessibleStateSet as = null; 8194 8195 if (ac != null) { 8196 as = ac.getAccessibleStateSet(); 8197 } 8198 if (as == null) { 8199 as = new AccessibleStateSet(); 8200 } 8201 Rectangle rjt = JTable.this.getVisibleRect(); 8202 Rectangle rcell = JTable.this.getCellRect(row, column, false); 8203 if (rjt.intersects(rcell)) { 8204 as.add(AccessibleState.SHOWING); 8205 } else { 8206 if (as.contains(AccessibleState.SHOWING)) { 8207 as.remove(AccessibleState.SHOWING); 8208 } 8209 } 8210 if (parent.isCellSelected(row, column)) { 8211 as.add(AccessibleState.SELECTED); 8212 } else if (as.contains(AccessibleState.SELECTED)) { 8213 as.remove(AccessibleState.SELECTED); 8214 } 8215 if ((row == getSelectedRow()) && (column == getSelectedColumn())) { 8216 as.add(AccessibleState.ACTIVE); 8217 } 8218 as.add(AccessibleState.TRANSIENT); 8219 return as; 8220 } 8221 8222 /** 8223 * Gets the <code>Accessible</code> parent of this object. 8224 * 8225 * @return the Accessible parent of this object; 8226 * <code>null</code> if this object does not 8227 * have an <code>Accessible</code> parent 8228 */ 8229 public Accessible getAccessibleParent() { 8230 return parent; 8231 } 8232 8233 /** 8234 * Gets the index of this object in its accessible parent. 8235 * 8236 * @return the index of this object in its parent; -1 if this 8237 * object does not have an accessible parent 8238 * @see #getAccessibleParent 8239 */ 8240 public int getAccessibleIndexInParent() { 8241 return index; 8242 } 8243 8244 /** 8245 * Returns the number of accessible children in the object. 8246 * 8247 * @return the number of accessible children in the object 8248 */ 8249 public int getAccessibleChildrenCount() { 8250 AccessibleContext ac = getCurrentAccessibleContext(); 8251 if (ac != null) { 8252 return ac.getAccessibleChildrenCount(); 8253 } else { 8254 return 0; 8255 } 8256 } 8257 8258 /** 8259 * Returns the specified <code>Accessible</code> child of the 8260 * object. 8261 * 8262 * @param i zero-based index of child 8263 * @return the <code>Accessible</code> child of the object 8264 */ 8265 public Accessible getAccessibleChild(int i) { 8266 AccessibleContext ac = getCurrentAccessibleContext(); 8267 if (ac != null) { 8268 Accessible accessibleChild = ac.getAccessibleChild(i); 8269 ac.setAccessibleParent(this); 8270 return accessibleChild; 8271 } else { 8272 return null; 8273 } 8274 } 8275 8276 /** 8277 * Gets the locale of the component. If the component 8278 * does not have a locale, then the locale of its parent 8279 * is returned. 8280 * 8281 * @return this component's locale; if this component does 8282 * not have a locale, the locale of its parent is returned 8283 * @exception IllegalComponentStateException if the 8284 * <code>Component</code> does not have its own locale 8285 * and has not yet been added to a containment hierarchy 8286 * such that the locale can be determined from the 8287 * containing parent 8288 * @see #setLocale 8289 */ 8290 public Locale getLocale() { 8291 AccessibleContext ac = getCurrentAccessibleContext(); 8292 if (ac != null) { 8293 return ac.getLocale(); 8294 } else { 8295 return null; 8296 } 8297 } 8298 8299 /** 8300 * Adds a <code>PropertyChangeListener</code> to the listener list. 8301 * The listener is registered for all properties. 8302 * 8303 * @param l the <code>PropertyChangeListener</code> 8304 * to be added 8305 */ 8306 public void addPropertyChangeListener(PropertyChangeListener l) { 8307 AccessibleContext ac = getCurrentAccessibleContext(); 8308 if (ac != null) { 8309 ac.addPropertyChangeListener(l); 8310 } else { 8311 super.addPropertyChangeListener(l); 8312 } 8313 } 8314 8315 /** 8316 * Removes a <code>PropertyChangeListener</code> from the 8317 * listener list. This removes a <code>PropertyChangeListener</code> 8318 * that was registered for all properties. 8319 * 8320 * @param l the <code>PropertyChangeListener</code> 8321 * to be removed 8322 */ 8323 public void removePropertyChangeListener(PropertyChangeListener l) { 8324 AccessibleContext ac = getCurrentAccessibleContext(); 8325 if (ac != null) { 8326 ac.removePropertyChangeListener(l); 8327 } else { 8328 super.removePropertyChangeListener(l); 8329 } 8330 } 8331 8332 /** 8333 * Gets the <code>AccessibleAction</code> associated with this 8334 * object if one exists. Otherwise returns <code>null</code>. 8335 * 8336 * @return the <code>AccessibleAction</code>, or <code>null</code> 8337 */ 8338 public AccessibleAction getAccessibleAction() { 8339 return getCurrentAccessibleContext().getAccessibleAction(); 8340 } 8341 8342 /** 8343 * Gets the <code>AccessibleComponent</code> associated with 8344 * this object if one exists. Otherwise returns <code>null</code>. 8345 * 8346 * @return the <code>AccessibleComponent</code>, or 8347 * <code>null</code> 8348 */ 8349 public AccessibleComponent getAccessibleComponent() { 8350 return this; // to override getBounds() 8351 } 8352 8353 /** 8354 * Gets the <code>AccessibleSelection</code> associated with 8355 * this object if one exists. Otherwise returns <code>null</code>. 8356 * 8357 * @return the <code>AccessibleSelection</code>, or 8358 * <code>null</code> 8359 */ 8360 public AccessibleSelection getAccessibleSelection() { 8361 return getCurrentAccessibleContext().getAccessibleSelection(); 8362 } 8363 8364 /** 8365 * Gets the <code>AccessibleText</code> associated with this 8366 * object if one exists. Otherwise returns <code>null</code>. 8367 * 8368 * @return the <code>AccessibleText</code>, or <code>null</code> 8369 */ 8370 public AccessibleText getAccessibleText() { 8371 return getCurrentAccessibleContext().getAccessibleText(); 8372 } 8373 8374 /** 8375 * Gets the <code>AccessibleValue</code> associated with 8376 * this object if one exists. Otherwise returns <code>null</code>. 8377 * 8378 * @return the <code>AccessibleValue</code>, or <code>null</code> 8379 */ 8380 public AccessibleValue getAccessibleValue() { 8381 return getCurrentAccessibleContext().getAccessibleValue(); 8382 } 8383 8384 8385 // AccessibleComponent methods 8386 8387 /** 8388 * Gets the background color of this object. 8389 * 8390 * @return the background color, if supported, of the object; 8391 * otherwise, <code>null</code> 8392 */ 8393 public Color getBackground() { 8394 AccessibleContext ac = getCurrentAccessibleContext(); 8395 if (ac instanceof AccessibleComponent) { 8396 return ((AccessibleComponent) ac).getBackground(); 8397 } else { 8398 Component c = getCurrentComponent(); 8399 if (c != null) { 8400 return c.getBackground(); 8401 } else { 8402 return null; 8403 } 8404 } 8405 } 8406 8407 /** 8408 * Sets the background color of this object. 8409 * 8410 * @param c the new <code>Color</code> for the background 8411 */ 8412 public void setBackground(Color c) { 8413 AccessibleContext ac = getCurrentAccessibleContext(); 8414 if (ac instanceof AccessibleComponent) { 8415 ((AccessibleComponent) ac).setBackground(c); 8416 } else { 8417 Component cp = getCurrentComponent(); 8418 if (cp != null) { 8419 cp.setBackground(c); 8420 } 8421 } 8422 } 8423 8424 /** 8425 * Gets the foreground color of this object. 8426 * 8427 * @return the foreground color, if supported, of the object; 8428 * otherwise, <code>null</code> 8429 */ 8430 public Color getForeground() { 8431 AccessibleContext ac = getCurrentAccessibleContext(); 8432 if (ac instanceof AccessibleComponent) { 8433 return ((AccessibleComponent) ac).getForeground(); 8434 } else { 8435 Component c = getCurrentComponent(); 8436 if (c != null) { 8437 return c.getForeground(); 8438 } else { 8439 return null; 8440 } 8441 } 8442 } 8443 8444 /** 8445 * Sets the foreground color of this object. 8446 * 8447 * @param c the new <code>Color</code> for the foreground 8448 */ 8449 public void setForeground(Color c) { 8450 AccessibleContext ac = getCurrentAccessibleContext(); 8451 if (ac instanceof AccessibleComponent) { 8452 ((AccessibleComponent) ac).setForeground(c); 8453 } else { 8454 Component cp = getCurrentComponent(); 8455 if (cp != null) { 8456 cp.setForeground(c); 8457 } 8458 } 8459 } 8460 8461 /** 8462 * Gets the <code>Cursor</code> of this object. 8463 * 8464 * @return the <code>Cursor</code>, if supported, 8465 * of the object; otherwise, <code>null</code> 8466 */ 8467 public Cursor getCursor() { 8468 AccessibleContext ac = getCurrentAccessibleContext(); 8469 if (ac instanceof AccessibleComponent) { 8470 return ((AccessibleComponent) ac).getCursor(); 8471 } else { 8472 Component c = getCurrentComponent(); 8473 if (c != null) { 8474 return c.getCursor(); 8475 } else { 8476 Accessible ap = getAccessibleParent(); 8477 if (ap instanceof AccessibleComponent) { 8478 return ((AccessibleComponent) ap).getCursor(); 8479 } else { 8480 return null; 8481 } 8482 } 8483 } 8484 } 8485 8486 /** 8487 * Sets the <code>Cursor</code> of this object. 8488 * 8489 * @param c the new <code>Cursor</code> for the object 8490 */ 8491 public void setCursor(Cursor c) { 8492 AccessibleContext ac = getCurrentAccessibleContext(); 8493 if (ac instanceof AccessibleComponent) { 8494 ((AccessibleComponent) ac).setCursor(c); 8495 } else { 8496 Component cp = getCurrentComponent(); 8497 if (cp != null) { 8498 cp.setCursor(c); 8499 } 8500 } 8501 } 8502 8503 /** 8504 * Gets the <code>Font</code> of this object. 8505 * 8506 * @return the <code>Font</code>,if supported, 8507 * for the object; otherwise, <code>null</code> 8508 */ 8509 public Font getFont() { 8510 AccessibleContext ac = getCurrentAccessibleContext(); 8511 if (ac instanceof AccessibleComponent) { 8512 return ((AccessibleComponent) ac).getFont(); 8513 } else { 8514 Component c = getCurrentComponent(); 8515 if (c != null) { 8516 return c.getFont(); 8517 } else { 8518 return null; 8519 } 8520 } 8521 } 8522 8523 /** 8524 * Sets the <code>Font</code> of this object. 8525 * 8526 * @param f the new <code>Font</code> for the object 8527 */ 8528 public void setFont(Font f) { 8529 AccessibleContext ac = getCurrentAccessibleContext(); 8530 if (ac instanceof AccessibleComponent) { 8531 ((AccessibleComponent) ac).setFont(f); 8532 } else { 8533 Component c = getCurrentComponent(); 8534 if (c != null) { 8535 c.setFont(f); 8536 } 8537 } 8538 } 8539 8540 /** 8541 * Gets the <code>FontMetrics</code> of this object. 8542 * 8543 * @param f the <code>Font</code> 8544 * @return the <code>FontMetrics</code> object, if supported; 8545 * otherwise <code>null</code> 8546 * @see #getFont 8547 */ 8548 public FontMetrics getFontMetrics(Font f) { 8549 AccessibleContext ac = getCurrentAccessibleContext(); 8550 if (ac instanceof AccessibleComponent) { 8551 return ((AccessibleComponent) ac).getFontMetrics(f); 8552 } else { 8553 Component c = getCurrentComponent(); 8554 if (c != null) { 8555 return c.getFontMetrics(f); 8556 } else { 8557 return null; 8558 } 8559 } 8560 } 8561 8562 /** 8563 * Determines if the object is enabled. 8564 * 8565 * @return true if object is enabled; otherwise, false 8566 */ 8567 public boolean isEnabled() { 8568 AccessibleContext ac = getCurrentAccessibleContext(); 8569 if (ac instanceof AccessibleComponent) { 8570 return ((AccessibleComponent) ac).isEnabled(); 8571 } else { 8572 Component c = getCurrentComponent(); 8573 if (c != null) { 8574 return c.isEnabled(); 8575 } else { 8576 return false; 8577 } 8578 } 8579 } 8580 8581 /** 8582 * Sets the enabled state of the object. 8583 * 8584 * @param b if true, enables this object; otherwise, disables it 8585 */ 8586 public void setEnabled(boolean b) { 8587 AccessibleContext ac = getCurrentAccessibleContext(); 8588 if (ac instanceof AccessibleComponent) { 8589 ((AccessibleComponent) ac).setEnabled(b); 8590 } else { 8591 Component c = getCurrentComponent(); 8592 if (c != null) { 8593 c.setEnabled(b); 8594 } 8595 } 8596 } 8597 8598 /** 8599 * Determines if this object is visible. Note: this means that the 8600 * object intends to be visible; however, it may not in fact be 8601 * showing on the screen because one of the objects that this object 8602 * is contained by is not visible. To determine if an object is 8603 * showing on the screen, use <code>isShowing</code>. 8604 * 8605 * @return true if object is visible; otherwise, false 8606 */ 8607 public boolean isVisible() { 8608 AccessibleContext ac = getCurrentAccessibleContext(); 8609 if (ac instanceof AccessibleComponent) { 8610 return ((AccessibleComponent) ac).isVisible(); 8611 } else { 8612 Component c = getCurrentComponent(); 8613 if (c != null) { 8614 return c.isVisible(); 8615 } else { 8616 return false; 8617 } 8618 } 8619 } 8620 8621 /** 8622 * Sets the visible state of the object. 8623 * 8624 * @param b if true, shows this object; otherwise, hides it 8625 */ 8626 public void setVisible(boolean b) { 8627 AccessibleContext ac = getCurrentAccessibleContext(); 8628 if (ac instanceof AccessibleComponent) { 8629 ((AccessibleComponent) ac).setVisible(b); 8630 } else { 8631 Component c = getCurrentComponent(); 8632 if (c != null) { 8633 c.setVisible(b); 8634 } 8635 } 8636 } 8637 8638 /** 8639 * Determines if the object is showing. This is determined 8640 * by checking the visibility of the object and ancestors 8641 * of the object. Note: this will return true even if the 8642 * object is obscured by another (for example, 8643 * it happens to be underneath a menu that was pulled down). 8644 * 8645 * @return true if the object is showing; otherwise, false 8646 */ 8647 public boolean isShowing() { 8648 AccessibleContext ac = getCurrentAccessibleContext(); 8649 if (ac instanceof AccessibleComponent) { 8650 if (ac.getAccessibleParent() != null) { 8651 return ((AccessibleComponent) ac).isShowing(); 8652 } else { 8653 // Fixes 4529616 - AccessibleJTableCell.isShowing() 8654 // returns false when the cell on the screen 8655 // if no parent 8656 return isVisible(); 8657 } 8658 } else { 8659 Component c = getCurrentComponent(); 8660 if (c != null) { 8661 return c.isShowing(); 8662 } else { 8663 return false; 8664 } 8665 } 8666 } 8667 8668 /** 8669 * Checks whether the specified point is within this 8670 * object's bounds, where the point's x and y coordinates 8671 * are defined to be relative to the coordinate system of 8672 * the object. 8673 * 8674 * @param p the <code>Point</code> relative to the 8675 * coordinate system of the object 8676 * @return true if object contains <code>Point</code>; 8677 * otherwise false 8678 */ 8679 public boolean contains(Point p) { 8680 AccessibleContext ac = getCurrentAccessibleContext(); 8681 if (ac instanceof AccessibleComponent) { 8682 Rectangle r = ((AccessibleComponent) ac).getBounds(); 8683 return r.contains(p); 8684 } else { 8685 Component c = getCurrentComponent(); 8686 if (c != null) { 8687 Rectangle r = c.getBounds(); 8688 return r.contains(p); 8689 } else { 8690 return getBounds().contains(p); 8691 } 8692 } 8693 } 8694 8695 /** 8696 * Returns the location of the object on the screen. 8697 * 8698 * @return location of object on screen -- can be 8699 * <code>null</code> if this object is not on the screen 8700 */ 8701 public Point getLocationOnScreen() { 8702 if (parent != null && parent.isShowing()) { 8703 Point parentLocation = parent.getLocationOnScreen(); 8704 Point componentLocation = getLocation(); 8705 componentLocation.translate(parentLocation.x, parentLocation.y); 8706 return componentLocation; 8707 } else { 8708 return null; 8709 } 8710 } 8711 8712 /** 8713 * Gets the location of the object relative to the parent 8714 * in the form of a point specifying the object's 8715 * top-left corner in the screen's coordinate space. 8716 * 8717 * @return an instance of <code>Point</code> representing 8718 * the top-left corner of the object's bounds in the 8719 * coordinate space of the screen; <code>null</code> if 8720 * this object or its parent are not on the screen 8721 */ 8722 public Point getLocation() { 8723 if (parent != null) { 8724 Rectangle r = parent.getCellRect(row, column, false); 8725 if (r != null) { 8726 return r.getLocation(); 8727 } 8728 } 8729 return null; 8730 } 8731 8732 /** 8733 * Sets the location of the object relative to the parent. 8734 */ 8735 public void setLocation(Point p) { 8736 // if ((parent != null) && (parent.contains(p))) { 8737 // ensureIndexIsVisible(indexInParent); 8738 // } 8739 } 8740 8741 public Rectangle getBounds() { 8742 if (parent != null) { 8743 return parent.getCellRect(row, column, false); 8744 } else { 8745 return null; 8746 } 8747 } 8748 8749 public void setBounds(Rectangle r) { 8750 AccessibleContext ac = getCurrentAccessibleContext(); 8751 if (ac instanceof AccessibleComponent) { 8752 ((AccessibleComponent) ac).setBounds(r); 8753 } else { 8754 Component c = getCurrentComponent(); 8755 if (c != null) { 8756 c.setBounds(r); 8757 } 8758 } 8759 } 8760 8761 public Dimension getSize() { 8762 if (parent != null) { 8763 Rectangle r = parent.getCellRect(row, column, false); 8764 if (r != null) { 8765 return r.getSize(); 8766 } 8767 } 8768 return null; 8769 } 8770 8771 public void setSize (Dimension d) { 8772 AccessibleContext ac = getCurrentAccessibleContext(); 8773 if (ac instanceof AccessibleComponent) { 8774 ((AccessibleComponent) ac).setSize(d); 8775 } else { 8776 Component c = getCurrentComponent(); 8777 if (c != null) { 8778 c.setSize(d); 8779 } 8780 } 8781 } 8782 8783 public Accessible getAccessibleAt(Point p) { 8784 AccessibleContext ac = getCurrentAccessibleContext(); 8785 if (ac instanceof AccessibleComponent) { 8786 return ((AccessibleComponent) ac).getAccessibleAt(p); 8787 } else { 8788 return null; 8789 } 8790 } 8791 8792 @SuppressWarnings("deprecation") 8793 public boolean isFocusTraversable() { 8794 AccessibleContext ac = getCurrentAccessibleContext(); 8795 if (ac instanceof AccessibleComponent) { 8796 return ((AccessibleComponent) ac).isFocusTraversable(); 8797 } else { 8798 Component c = getCurrentComponent(); 8799 if (c != null) { 8800 return c.isFocusTraversable(); 8801 } else { 8802 return false; 8803 } 8804 } 8805 } 8806 8807 public void requestFocus() { 8808 AccessibleContext ac = getCurrentAccessibleContext(); 8809 if (ac instanceof AccessibleComponent) { 8810 ((AccessibleComponent) ac).requestFocus(); 8811 } else { 8812 Component c = getCurrentComponent(); 8813 if (c != null) { 8814 c.requestFocus(); 8815 } 8816 } 8817 } 8818 8819 public void addFocusListener(FocusListener l) { 8820 AccessibleContext ac = getCurrentAccessibleContext(); 8821 if (ac instanceof AccessibleComponent) { 8822 ((AccessibleComponent) ac).addFocusListener(l); 8823 } else { 8824 Component c = getCurrentComponent(); 8825 if (c != null) { 8826 c.addFocusListener(l); 8827 } 8828 } 8829 } 8830 8831 public void removeFocusListener(FocusListener l) { 8832 AccessibleContext ac = getCurrentAccessibleContext(); 8833 if (ac instanceof AccessibleComponent) { 8834 ((AccessibleComponent) ac).removeFocusListener(l); 8835 } else { 8836 Component c = getCurrentComponent(); 8837 if (c != null) { 8838 c.removeFocusListener(l); 8839 } 8840 } 8841 } 8842 8843 } // inner class AccessibleJTableCell 8844 8845 // Begin AccessibleJTableHeader ========== // TIGER - 4715503 8846 8847 /** 8848 * This class implements accessibility for JTable header cells. 8849 */ 8850 private class AccessibleJTableHeaderCell extends AccessibleContext 8851 implements Accessible, AccessibleComponent { 8852 8853 private int row; 8854 private int column; 8855 private JTableHeader parent; 8856 private Component rendererComponent; 8857 8858 /** 8859 * Constructs an <code>AccessibleJTableHeaderEntry</code> instance. 8860 * 8861 * @param row header cell row index 8862 * @param column header cell column index 8863 * @param parent header cell parent 8864 * @param rendererComponent component that renders the header cell 8865 */ 8866 public AccessibleJTableHeaderCell(int row, int column, 8867 JTableHeader parent, 8868 Component rendererComponent) { 8869 this.row = row; 8870 this.column = column; 8871 this.parent = parent; 8872 this.rendererComponent = rendererComponent; 8873 this.setAccessibleParent(parent); 8874 } 8875 8876 /** 8877 * Gets the <code>AccessibleContext</code> associated with this 8878 * component. In the implementation of the Java Accessibility 8879 * API for this class, return this object, which is its own 8880 * <code>AccessibleContext</code>. 8881 * 8882 * @return this object 8883 */ 8884 public AccessibleContext getAccessibleContext() { 8885 return this; 8886 } 8887 8888 /* 8889 * Returns the AccessibleContext for the header cell 8890 * renderer. 8891 */ 8892 private AccessibleContext getCurrentAccessibleContext() { 8893 return rendererComponent.getAccessibleContext(); 8894 } 8895 8896 /* 8897 * Returns the component that renders the header cell. 8898 */ 8899 private Component getCurrentComponent() { 8900 return rendererComponent; 8901 } 8902 8903 // AccessibleContext methods ========== 8904 8905 /** 8906 * Gets the accessible name of this object. 8907 * 8908 * @return the localized name of the object; <code>null</code> 8909 * if this object does not have a name 8910 */ 8911 public String getAccessibleName() { 8912 AccessibleContext ac = getCurrentAccessibleContext(); 8913 if (ac != null) { 8914 String name = ac.getAccessibleName(); 8915 if ((name != null) && (name != "")) { 8916 return ac.getAccessibleName(); 8917 } 8918 } 8919 if ((accessibleName != null) && (accessibleName != "")) { 8920 return accessibleName; 8921 } else { 8922 return null; 8923 } 8924 } 8925 8926 /** 8927 * Sets the localized accessible name of this object. 8928 * 8929 * @param s the new localized name of the object 8930 */ 8931 public void setAccessibleName(String s) { 8932 AccessibleContext ac = getCurrentAccessibleContext(); 8933 if (ac != null) { 8934 ac.setAccessibleName(s); 8935 } else { 8936 super.setAccessibleName(s); 8937 } 8938 } 8939 8940 /** 8941 * Gets the accessible description of this object. 8942 * 8943 * @return the localized description of the object; 8944 * <code>null</code> if this object does not have 8945 * a description 8946 */ 8947 public String getAccessibleDescription() { 8948 AccessibleContext ac = getCurrentAccessibleContext(); 8949 if (ac != null) { 8950 return ac.getAccessibleDescription(); 8951 } else { 8952 return super.getAccessibleDescription(); 8953 } 8954 } 8955 8956 /** 8957 * Sets the accessible description of this object. 8958 * 8959 * @param s the new localized description of the object 8960 */ 8961 public void setAccessibleDescription(String s) { 8962 AccessibleContext ac = getCurrentAccessibleContext(); 8963 if (ac != null) { 8964 ac.setAccessibleDescription(s); 8965 } else { 8966 super.setAccessibleDescription(s); 8967 } 8968 } 8969 8970 /** 8971 * Gets the role of this object. 8972 * 8973 * @return an instance of <code>AccessibleRole</code> 8974 * describing the role of the object 8975 * @see AccessibleRole 8976 */ 8977 public AccessibleRole getAccessibleRole() { 8978 AccessibleContext ac = getCurrentAccessibleContext(); 8979 if (ac != null) { 8980 return ac.getAccessibleRole(); 8981 } else { 8982 return AccessibleRole.UNKNOWN; 8983 } 8984 } 8985 8986 /** 8987 * Gets the state set of this object. 8988 * 8989 * @return an instance of <code>AccessibleStateSet</code> 8990 * containing the current state set of the object 8991 * @see AccessibleState 8992 */ 8993 public AccessibleStateSet getAccessibleStateSet() { 8994 AccessibleContext ac = getCurrentAccessibleContext(); 8995 AccessibleStateSet as = null; 8996 8997 if (ac != null) { 8998 as = ac.getAccessibleStateSet(); 8999 } 9000 if (as == null) { 9001 as = new AccessibleStateSet(); 9002 } 9003 Rectangle rjt = JTable.this.getVisibleRect(); 9004 Rectangle rcell = JTable.this.getCellRect(row, column, false); 9005 if (rjt.intersects(rcell)) { 9006 as.add(AccessibleState.SHOWING); 9007 } else { 9008 if (as.contains(AccessibleState.SHOWING)) { 9009 as.remove(AccessibleState.SHOWING); 9010 } 9011 } 9012 if (JTable.this.isCellSelected(row, column)) { 9013 as.add(AccessibleState.SELECTED); 9014 } else if (as.contains(AccessibleState.SELECTED)) { 9015 as.remove(AccessibleState.SELECTED); 9016 } 9017 if ((row == getSelectedRow()) && (column == getSelectedColumn())) { 9018 as.add(AccessibleState.ACTIVE); 9019 } 9020 as.add(AccessibleState.TRANSIENT); 9021 return as; 9022 } 9023 9024 /** 9025 * Gets the <code>Accessible</code> parent of this object. 9026 * 9027 * @return the Accessible parent of this object; 9028 * <code>null</code> if this object does not 9029 * have an <code>Accessible</code> parent 9030 */ 9031 public Accessible getAccessibleParent() { 9032 return parent; 9033 } 9034 9035 /** 9036 * Gets the index of this object in its accessible parent. 9037 * 9038 * @return the index of this object in its parent; -1 if this 9039 * object does not have an accessible parent 9040 * @see #getAccessibleParent 9041 */ 9042 public int getAccessibleIndexInParent() { 9043 return column; 9044 } 9045 9046 /** 9047 * Returns the number of accessible children in the object. 9048 * 9049 * @return the number of accessible children in the object 9050 */ 9051 public int getAccessibleChildrenCount() { 9052 AccessibleContext ac = getCurrentAccessibleContext(); 9053 if (ac != null) { 9054 return ac.getAccessibleChildrenCount(); 9055 } else { 9056 return 0; 9057 } 9058 } 9059 9060 /** 9061 * Returns the specified <code>Accessible</code> child of the 9062 * object. 9063 * 9064 * @param i zero-based index of child 9065 * @return the <code>Accessible</code> child of the object 9066 */ 9067 public Accessible getAccessibleChild(int i) { 9068 AccessibleContext ac = getCurrentAccessibleContext(); 9069 if (ac != null) { 9070 Accessible accessibleChild = ac.getAccessibleChild(i); 9071 ac.setAccessibleParent(this); 9072 return accessibleChild; 9073 } else { 9074 return null; 9075 } 9076 } 9077 9078 /** 9079 * Gets the locale of the component. If the component 9080 * does not have a locale, then the locale of its parent 9081 * is returned. 9082 * 9083 * @return this component's locale; if this component does 9084 * not have a locale, the locale of its parent is returned 9085 * @exception IllegalComponentStateException if the 9086 * <code>Component</code> does not have its own locale 9087 * and has not yet been added to a containment hierarchy 9088 * such that the locale can be determined from the 9089 * containing parent 9090 * @see #setLocale 9091 */ 9092 public Locale getLocale() { 9093 AccessibleContext ac = getCurrentAccessibleContext(); 9094 if (ac != null) { 9095 return ac.getLocale(); 9096 } else { 9097 return null; 9098 } 9099 } 9100 9101 /** 9102 * Adds a <code>PropertyChangeListener</code> to the listener list. 9103 * The listener is registered for all properties. 9104 * 9105 * @param l the <code>PropertyChangeListener</code> 9106 * to be added 9107 */ 9108 public void addPropertyChangeListener(PropertyChangeListener l) { 9109 AccessibleContext ac = getCurrentAccessibleContext(); 9110 if (ac != null) { 9111 ac.addPropertyChangeListener(l); 9112 } else { 9113 super.addPropertyChangeListener(l); 9114 } 9115 } 9116 9117 /** 9118 * Removes a <code>PropertyChangeListener</code> from the 9119 * listener list. This removes a <code>PropertyChangeListener</code> 9120 * that was registered for all properties. 9121 * 9122 * @param l the <code>PropertyChangeListener</code> 9123 * to be removed 9124 */ 9125 public void removePropertyChangeListener(PropertyChangeListener l) { 9126 AccessibleContext ac = getCurrentAccessibleContext(); 9127 if (ac != null) { 9128 ac.removePropertyChangeListener(l); 9129 } else { 9130 super.removePropertyChangeListener(l); 9131 } 9132 } 9133 9134 /** 9135 * Gets the <code>AccessibleAction</code> associated with this 9136 * object if one exists. Otherwise returns <code>null</code>. 9137 * 9138 * @return the <code>AccessibleAction</code>, or <code>null</code> 9139 */ 9140 public AccessibleAction getAccessibleAction() { 9141 return getCurrentAccessibleContext().getAccessibleAction(); 9142 } 9143 9144 /** 9145 * Gets the <code>AccessibleComponent</code> associated with 9146 * this object if one exists. Otherwise returns <code>null</code>. 9147 * 9148 * @return the <code>AccessibleComponent</code>, or 9149 * <code>null</code> 9150 */ 9151 public AccessibleComponent getAccessibleComponent() { 9152 return this; // to override getBounds() 9153 } 9154 9155 /** 9156 * Gets the <code>AccessibleSelection</code> associated with 9157 * this object if one exists. Otherwise returns <code>null</code>. 9158 * 9159 * @return the <code>AccessibleSelection</code>, or 9160 * <code>null</code> 9161 */ 9162 public AccessibleSelection getAccessibleSelection() { 9163 return getCurrentAccessibleContext().getAccessibleSelection(); 9164 } 9165 9166 /** 9167 * Gets the <code>AccessibleText</code> associated with this 9168 * object if one exists. Otherwise returns <code>null</code>. 9169 * 9170 * @return the <code>AccessibleText</code>, or <code>null</code> 9171 */ 9172 public AccessibleText getAccessibleText() { 9173 return getCurrentAccessibleContext().getAccessibleText(); 9174 } 9175 9176 /** 9177 * Gets the <code>AccessibleValue</code> associated with 9178 * this object if one exists. Otherwise returns <code>null</code>. 9179 * 9180 * @return the <code>AccessibleValue</code>, or <code>null</code> 9181 */ 9182 public AccessibleValue getAccessibleValue() { 9183 return getCurrentAccessibleContext().getAccessibleValue(); 9184 } 9185 9186 9187 // AccessibleComponent methods ========== 9188 9189 /** 9190 * Gets the background color of this object. 9191 * 9192 * @return the background color, if supported, of the object; 9193 * otherwise, <code>null</code> 9194 */ 9195 public Color getBackground() { 9196 AccessibleContext ac = getCurrentAccessibleContext(); 9197 if (ac instanceof AccessibleComponent) { 9198 return ((AccessibleComponent) ac).getBackground(); 9199 } else { 9200 Component c = getCurrentComponent(); 9201 if (c != null) { 9202 return c.getBackground(); 9203 } else { 9204 return null; 9205 } 9206 } 9207 } 9208 9209 /** 9210 * Sets the background color of this object. 9211 * 9212 * @param c the new <code>Color</code> for the background 9213 */ 9214 public void setBackground(Color c) { 9215 AccessibleContext ac = getCurrentAccessibleContext(); 9216 if (ac instanceof AccessibleComponent) { 9217 ((AccessibleComponent) ac).setBackground(c); 9218 } else { 9219 Component cp = getCurrentComponent(); 9220 if (cp != null) { 9221 cp.setBackground(c); 9222 } 9223 } 9224 } 9225 9226 /** 9227 * Gets the foreground color of this object. 9228 * 9229 * @return the foreground color, if supported, of the object; 9230 * otherwise, <code>null</code> 9231 */ 9232 public Color getForeground() { 9233 AccessibleContext ac = getCurrentAccessibleContext(); 9234 if (ac instanceof AccessibleComponent) { 9235 return ((AccessibleComponent) ac).getForeground(); 9236 } else { 9237 Component c = getCurrentComponent(); 9238 if (c != null) { 9239 return c.getForeground(); 9240 } else { 9241 return null; 9242 } 9243 } 9244 } 9245 9246 /** 9247 * Sets the foreground color of this object. 9248 * 9249 * @param c the new <code>Color</code> for the foreground 9250 */ 9251 public void setForeground(Color c) { 9252 AccessibleContext ac = getCurrentAccessibleContext(); 9253 if (ac instanceof AccessibleComponent) { 9254 ((AccessibleComponent) ac).setForeground(c); 9255 } else { 9256 Component cp = getCurrentComponent(); 9257 if (cp != null) { 9258 cp.setForeground(c); 9259 } 9260 } 9261 } 9262 9263 /** 9264 * Gets the <code>Cursor</code> of this object. 9265 * 9266 * @return the <code>Cursor</code>, if supported, 9267 * of the object; otherwise, <code>null</code> 9268 */ 9269 public Cursor getCursor() { 9270 AccessibleContext ac = getCurrentAccessibleContext(); 9271 if (ac instanceof AccessibleComponent) { 9272 return ((AccessibleComponent) ac).getCursor(); 9273 } else { 9274 Component c = getCurrentComponent(); 9275 if (c != null) { 9276 return c.getCursor(); 9277 } else { 9278 Accessible ap = getAccessibleParent(); 9279 if (ap instanceof AccessibleComponent) { 9280 return ((AccessibleComponent) ap).getCursor(); 9281 } else { 9282 return null; 9283 } 9284 } 9285 } 9286 } 9287 9288 /** 9289 * Sets the <code>Cursor</code> of this object. 9290 * 9291 * @param c the new <code>Cursor</code> for the object 9292 */ 9293 public void setCursor(Cursor c) { 9294 AccessibleContext ac = getCurrentAccessibleContext(); 9295 if (ac instanceof AccessibleComponent) { 9296 ((AccessibleComponent) ac).setCursor(c); 9297 } else { 9298 Component cp = getCurrentComponent(); 9299 if (cp != null) { 9300 cp.setCursor(c); 9301 } 9302 } 9303 } 9304 9305 /** 9306 * Gets the <code>Font</code> of this object. 9307 * 9308 * @return the <code>Font</code>,if supported, 9309 * for the object; otherwise, <code>null</code> 9310 */ 9311 public Font getFont() { 9312 AccessibleContext ac = getCurrentAccessibleContext(); 9313 if (ac instanceof AccessibleComponent) { 9314 return ((AccessibleComponent) ac).getFont(); 9315 } else { 9316 Component c = getCurrentComponent(); 9317 if (c != null) { 9318 return c.getFont(); 9319 } else { 9320 return null; 9321 } 9322 } 9323 } 9324 9325 /** 9326 * Sets the <code>Font</code> of this object. 9327 * 9328 * @param f the new <code>Font</code> for the object 9329 */ 9330 public void setFont(Font f) { 9331 AccessibleContext ac = getCurrentAccessibleContext(); 9332 if (ac instanceof AccessibleComponent) { 9333 ((AccessibleComponent) ac).setFont(f); 9334 } else { 9335 Component c = getCurrentComponent(); 9336 if (c != null) { 9337 c.setFont(f); 9338 } 9339 } 9340 } 9341 9342 /** 9343 * Gets the <code>FontMetrics</code> of this object. 9344 * 9345 * @param f the <code>Font</code> 9346 * @return the <code>FontMetrics</code> object, if supported; 9347 * otherwise <code>null</code> 9348 * @see #getFont 9349 */ 9350 public FontMetrics getFontMetrics(Font f) { 9351 AccessibleContext ac = getCurrentAccessibleContext(); 9352 if (ac instanceof AccessibleComponent) { 9353 return ((AccessibleComponent) ac).getFontMetrics(f); 9354 } else { 9355 Component c = getCurrentComponent(); 9356 if (c != null) { 9357 return c.getFontMetrics(f); 9358 } else { 9359 return null; 9360 } 9361 } 9362 } 9363 9364 /** 9365 * Determines if the object is enabled. 9366 * 9367 * @return true if object is enabled; otherwise, false 9368 */ 9369 public boolean isEnabled() { 9370 AccessibleContext ac = getCurrentAccessibleContext(); 9371 if (ac instanceof AccessibleComponent) { 9372 return ((AccessibleComponent) ac).isEnabled(); 9373 } else { 9374 Component c = getCurrentComponent(); 9375 if (c != null) { 9376 return c.isEnabled(); 9377 } else { 9378 return false; 9379 } 9380 } 9381 } 9382 9383 /** 9384 * Sets the enabled state of the object. 9385 * 9386 * @param b if true, enables this object; otherwise, disables it 9387 */ 9388 public void setEnabled(boolean b) { 9389 AccessibleContext ac = getCurrentAccessibleContext(); 9390 if (ac instanceof AccessibleComponent) { 9391 ((AccessibleComponent) ac).setEnabled(b); 9392 } else { 9393 Component c = getCurrentComponent(); 9394 if (c != null) { 9395 c.setEnabled(b); 9396 } 9397 } 9398 } 9399 9400 /** 9401 * Determines if this object is visible. Note: this means that the 9402 * object intends to be visible; however, it may not in fact be 9403 * showing on the screen because one of the objects that this object 9404 * is contained by is not visible. To determine if an object is 9405 * showing on the screen, use <code>isShowing</code>. 9406 * 9407 * @return true if object is visible; otherwise, false 9408 */ 9409 public boolean isVisible() { 9410 AccessibleContext ac = getCurrentAccessibleContext(); 9411 if (ac instanceof AccessibleComponent) { 9412 return ((AccessibleComponent) ac).isVisible(); 9413 } else { 9414 Component c = getCurrentComponent(); 9415 if (c != null) { 9416 return c.isVisible(); 9417 } else { 9418 return false; 9419 } 9420 } 9421 } 9422 9423 /** 9424 * Sets the visible state of the object. 9425 * 9426 * @param b if true, shows this object; otherwise, hides it 9427 */ 9428 public void setVisible(boolean b) { 9429 AccessibleContext ac = getCurrentAccessibleContext(); 9430 if (ac instanceof AccessibleComponent) { 9431 ((AccessibleComponent) ac).setVisible(b); 9432 } else { 9433 Component c = getCurrentComponent(); 9434 if (c != null) { 9435 c.setVisible(b); 9436 } 9437 } 9438 } 9439 9440 /** 9441 * Determines if the object is showing. This is determined 9442 * by checking the visibility of the object and ancestors 9443 * of the object. Note: this will return true even if the 9444 * object is obscured by another (for example, 9445 * it happens to be underneath a menu that was pulled down). 9446 * 9447 * @return true if the object is showing; otherwise, false 9448 */ 9449 public boolean isShowing() { 9450 AccessibleContext ac = getCurrentAccessibleContext(); 9451 if (ac instanceof AccessibleComponent) { 9452 if (ac.getAccessibleParent() != null) { 9453 return ((AccessibleComponent) ac).isShowing(); 9454 } else { 9455 // Fixes 4529616 - AccessibleJTableCell.isShowing() 9456 // returns false when the cell on the screen 9457 // if no parent 9458 return isVisible(); 9459 } 9460 } else { 9461 Component c = getCurrentComponent(); 9462 if (c != null) { 9463 return c.isShowing(); 9464 } else { 9465 return false; 9466 } 9467 } 9468 } 9469 9470 /** 9471 * Checks whether the specified point is within this 9472 * object's bounds, where the point's x and y coordinates 9473 * are defined to be relative to the coordinate system of 9474 * the object. 9475 * 9476 * @param p the <code>Point</code> relative to the 9477 * coordinate system of the object 9478 * @return true if object contains <code>Point</code>; 9479 * otherwise false 9480 */ 9481 public boolean contains(Point p) { 9482 AccessibleContext ac = getCurrentAccessibleContext(); 9483 if (ac instanceof AccessibleComponent) { 9484 Rectangle r = ((AccessibleComponent) ac).getBounds(); 9485 return r.contains(p); 9486 } else { 9487 Component c = getCurrentComponent(); 9488 if (c != null) { 9489 Rectangle r = c.getBounds(); 9490 return r.contains(p); 9491 } else { 9492 return getBounds().contains(p); 9493 } 9494 } 9495 } 9496 9497 /** 9498 * Returns the location of the object on the screen. 9499 * 9500 * @return location of object on screen -- can be 9501 * <code>null</code> if this object is not on the screen 9502 */ 9503 public Point getLocationOnScreen() { 9504 if (parent != null && parent.isShowing()) { 9505 Point parentLocation = parent.getLocationOnScreen(); 9506 Point componentLocation = getLocation(); 9507 componentLocation.translate(parentLocation.x, parentLocation.y); 9508 return componentLocation; 9509 } else { 9510 return null; 9511 } 9512 } 9513 9514 /** 9515 * Gets the location of the object relative to the parent 9516 * in the form of a point specifying the object's 9517 * top-left corner in the screen's coordinate space. 9518 * 9519 * @return an instance of <code>Point</code> representing 9520 * the top-left corner of the object's bounds in the 9521 * coordinate space of the screen; <code>null</code> if 9522 * this object or its parent are not on the screen 9523 */ 9524 public Point getLocation() { 9525 if (parent != null) { 9526 Rectangle r = parent.getHeaderRect(column); 9527 if (r != null) { 9528 return r.getLocation(); 9529 } 9530 } 9531 return null; 9532 } 9533 9534 /** 9535 * Sets the location of the object relative to the parent. 9536 * @param p the new position for the top-left corner 9537 * @see #getLocation 9538 */ 9539 public void setLocation(Point p) { 9540 } 9541 9542 /** 9543 * Gets the bounds of this object in the form of a Rectangle object. 9544 * The bounds specify this object's width, height, and location 9545 * relative to its parent. 9546 * 9547 * @return A rectangle indicating this component's bounds; null if 9548 * this object is not on the screen. 9549 * @see #contains 9550 */ 9551 public Rectangle getBounds() { 9552 if (parent != null) { 9553 return parent.getHeaderRect(column); 9554 } else { 9555 return null; 9556 } 9557 } 9558 9559 /** 9560 * Sets the bounds of this object in the form of a Rectangle object. 9561 * The bounds specify this object's width, height, and location 9562 * relative to its parent. 9563 * 9564 * @param r rectangle indicating this component's bounds 9565 * @see #getBounds 9566 */ 9567 public void setBounds(Rectangle r) { 9568 AccessibleContext ac = getCurrentAccessibleContext(); 9569 if (ac instanceof AccessibleComponent) { 9570 ((AccessibleComponent) ac).setBounds(r); 9571 } else { 9572 Component c = getCurrentComponent(); 9573 if (c != null) { 9574 c.setBounds(r); 9575 } 9576 } 9577 } 9578 9579 /** 9580 * Returns the size of this object in the form of a Dimension object. 9581 * The height field of the Dimension object contains this object's 9582 * height, and the width field of the Dimension object contains this 9583 * object's width. 9584 * 9585 * @return A Dimension object that indicates the size of this component; 9586 * null if this object is not on the screen 9587 * @see #setSize 9588 */ 9589 public Dimension getSize() { 9590 if (parent != null) { 9591 Rectangle r = parent.getHeaderRect(column); 9592 if (r != null) { 9593 return r.getSize(); 9594 } 9595 } 9596 return null; 9597 } 9598 9599 /** 9600 * Resizes this object so that it has width and height. 9601 * 9602 * @param d The dimension specifying the new size of the object. 9603 * @see #getSize 9604 */ 9605 public void setSize (Dimension d) { 9606 AccessibleContext ac = getCurrentAccessibleContext(); 9607 if (ac instanceof AccessibleComponent) { 9608 ((AccessibleComponent) ac).setSize(d); 9609 } else { 9610 Component c = getCurrentComponent(); 9611 if (c != null) { 9612 c.setSize(d); 9613 } 9614 } 9615 } 9616 9617 /** 9618 * Returns the Accessible child, if one exists, contained at the local 9619 * coordinate Point. 9620 * 9621 * @param p The point relative to the coordinate system of this object. 9622 * @return the Accessible, if it exists, at the specified location; 9623 * otherwise null 9624 */ 9625 public Accessible getAccessibleAt(Point p) { 9626 AccessibleContext ac = getCurrentAccessibleContext(); 9627 if (ac instanceof AccessibleComponent) { 9628 return ((AccessibleComponent) ac).getAccessibleAt(p); 9629 } else { 9630 return null; 9631 } 9632 } 9633 9634 /** 9635 * Returns whether this object can accept focus or not. Objects that 9636 * can accept focus will also have the AccessibleState.FOCUSABLE state 9637 * set in their AccessibleStateSets. 9638 * 9639 * @return true if object can accept focus; otherwise false 9640 * @see AccessibleContext#getAccessibleStateSet 9641 * @see AccessibleState#FOCUSABLE 9642 * @see AccessibleState#FOCUSED 9643 * @see AccessibleStateSet 9644 */ 9645 @SuppressWarnings("deprecation") 9646 public boolean isFocusTraversable() { 9647 AccessibleContext ac = getCurrentAccessibleContext(); 9648 if (ac instanceof AccessibleComponent) { 9649 return ((AccessibleComponent) ac).isFocusTraversable(); 9650 } else { 9651 Component c = getCurrentComponent(); 9652 if (c != null) { 9653 return c.isFocusTraversable(); 9654 } else { 9655 return false; 9656 } 9657 } 9658 } 9659 9660 /** 9661 * Requests focus for this object. If this object cannot accept focus, 9662 * nothing will happen. Otherwise, the object will attempt to take 9663 * focus. 9664 * @see #isFocusTraversable 9665 */ 9666 public void requestFocus() { 9667 AccessibleContext ac = getCurrentAccessibleContext(); 9668 if (ac instanceof AccessibleComponent) { 9669 ((AccessibleComponent) ac).requestFocus(); 9670 } else { 9671 Component c = getCurrentComponent(); 9672 if (c != null) { 9673 c.requestFocus(); 9674 } 9675 } 9676 } 9677 9678 /** 9679 * Adds the specified focus listener to receive focus events from this 9680 * component. 9681 * 9682 * @param l the focus listener 9683 * @see #removeFocusListener 9684 */ 9685 public void addFocusListener(FocusListener l) { 9686 AccessibleContext ac = getCurrentAccessibleContext(); 9687 if (ac instanceof AccessibleComponent) { 9688 ((AccessibleComponent) ac).addFocusListener(l); 9689 } else { 9690 Component c = getCurrentComponent(); 9691 if (c != null) { 9692 c.addFocusListener(l); 9693 } 9694 } 9695 } 9696 9697 /** 9698 * Removes the specified focus listener so it no longer receives focus 9699 * events from this component. 9700 * 9701 * @param l the focus listener 9702 * @see #addFocusListener 9703 */ 9704 public void removeFocusListener(FocusListener l) { 9705 AccessibleContext ac = getCurrentAccessibleContext(); 9706 if (ac instanceof AccessibleComponent) { 9707 ((AccessibleComponent) ac).removeFocusListener(l); 9708 } else { 9709 Component c = getCurrentComponent(); 9710 if (c != null) { 9711 c.removeFocusListener(l); 9712 } 9713 } 9714 } 9715 9716 } // inner class AccessibleJTableHeaderCell 9717 9718 } // inner class AccessibleJTable 9719 9720 } // End of Class JTable