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