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