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