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