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