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