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