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