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