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