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