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