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