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