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