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