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