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