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