History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/javax/swing/JTable.java (1 of 18352 files). Show all

Unified · Split

java/pkg/javax/swing/JTable.java+9607−0
@@ -0,0 +1,9607 @@
1+/*
2+ * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package javax.swing;
27+
28+import java.util.*;
29+
30+import java.applet.Applet;
31+import java.awt.*;
32+import java.awt.event.*;
33+import java.awt.print.*;
34+
35+import java.beans.*;
36+
37+import java.io.Serializable;
38+import java.io.ObjectOutputStream;
39+import java.io.ObjectInputStream;
40+import java.io.IOException;
41+
42+import javax.accessibility.*;
43+
44+import javax.swing.event.*;
45+import javax.swing.plaf.*;
46+import javax.swing.table.*;
47+import javax.swing.border.*;
48+
49+import java.text.NumberFormat;
50+import java.text.DateFormat;
51+import java.text.MessageFormat;
52+
53+import javax.print.attribute.*;
54+import javax.print.PrintService;
55+
56+import sun.swing.SwingUtilities2;
57+import sun.swing.SwingUtilities2.Section;
58+import static sun.swing.SwingUtilities2.Section.*;
59+import sun.swing.PrintingStatus;
60+import sun.swing.SwingLazyValue;
61+
62+/**
63+ * The <code>JTable</code> is used to display and edit regular two-dimensional tables
64+ * of cells.
65+ * See <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/table.html">How to Use Tables</a>
66+ * in <em>The Java Tutorial</em>
67+ * for task-oriented documentation and examples of using <code>JTable</code>.
68+ *
69+ * <p>
70+ * The <code>JTable</code> has many
71+ * facilities that make it possible to customize its rendering and editing
72+ * but provides defaults for these features so that simple tables can be
73+ * set up easily. For example, to set up a table with 10 rows and 10
74+ * columns of numbers:
75+ * <p>
76+ * <pre>
77+ * TableModel dataModel = new AbstractTableModel() {
78+ * public int getColumnCount() { return 10; }
79+ * public int getRowCount() { return 10;}
80+ * public Object getValueAt(int row, int col) { return new Integer(row*col); }
81+ * };
82+ * JTable table = new JTable(dataModel);
83+ * JScrollPane scrollpane = new JScrollPane(table);
84+ * </pre>
85+ * <p>
86+ * {@code JTable}s are typically placed inside of a {@code JScrollPane}. By
87+ * default, a {@code JTable} will adjust its width such that
88+ * a horizontal scrollbar is unnecessary. To allow for a horizontal scrollbar,
89+ * invoke {@link #setAutoResizeMode} with {@code AUTO_RESIZE_OFF}.
90+ * Note that if you wish to use a <code>JTable</code> in a standalone
91+ * view (outside of a <code>JScrollPane</code>) and want the header
92+ * displayed, you can get it using {@link #getTableHeader} and
93+ * display it separately.
94+ * <p>
95+ * To enable sorting and filtering of rows, use a
96+ * {@code RowSorter}.
97+ * You can set up a row sorter in either of two ways:
98+ * <ul>
99+ * <li>Directly set the {@code RowSorter}. For example:
100+ * {@code table.setRowSorter(new TableRowSorter(model))}.
101+ * <li>Set the {@code autoCreateRowSorter}
102+ * property to {@code true}, so that the {@code JTable}
103+ * creates a {@code RowSorter} for
104+ * you. For example: {@code setAutoCreateRowSorter(true)}.
105+ * </ul>
106+ * <p>
107+ * When designing applications that use the <code>JTable</code> it is worth paying
108+ * close attention to the data structures that will represent the table's data.
109+ * The <code>DefaultTableModel</code> is a model implementation that
110+ * uses a <code>Vector</code> of <code>Vector</code>s of <code>Object</code>s to
111+ * store the cell values. As well as copying the data from an
112+ * application into the <code>DefaultTableModel</code>,
113+ * it is also possible to wrap the data in the methods of the
114+ * <code>TableModel</code> interface so that the data can be passed to the
115+ * <code>JTable</code> directly, as in the example above. This often results
116+ * in more efficient applications because the model is free to choose the
117+ * internal representation that best suits the data.
118+ * A good rule of thumb for deciding whether to use the <code>AbstractTableModel</code>
119+ * or the <code>DefaultTableModel</code> is to use the <code>AbstractTableModel</code>
120+ * as the base class for creating subclasses and the <code>DefaultTableModel</code>
121+ * when subclassing is not required.
122+ * <p>
123+ * The "TableExample" directory in the demo area of the source distribution
124+ * gives a number of complete examples of <code>JTable</code> usage,
125+ * covering how the <code>JTable</code> can be used to provide an
126+ * editable view of data taken from a database and how to modify
127+ * the columns in the display to use specialized renderers and editors.
128+ * <p>
129+ * The <code>JTable</code> uses integers exclusively to refer to both the rows and the columns
130+ * of the model that it displays. The <code>JTable</code> simply takes a tabular range of cells
131+ * and uses <code>getValueAt(int, int)</code> to retrieve the
132+ * values from the model during painting. It is important to remember that
133+ * the column and row indexes returned by various <code>JTable</code> methods
134+ * are in terms of the <code>JTable</code> (the view) and are not
135+ * necessarily the same indexes used by the model.
136+ * <p>
137+ * By default, columns may be rearranged in the <code>JTable</code> so that the
138+ * view's columns appear in a different order to the columns in the model.
139+ * This does not affect the implementation of the model at all: when the
140+ * columns are reordered, the <code>JTable</code> maintains the new order of the columns
141+ * internally and converts its column indices before querying the model.
142+ * <p>
143+ * So, when writing a <code>TableModel</code>, it is not necessary to listen for column
144+ * reordering events as the model will be queried in its own coordinate
145+ * system regardless of what is happening in the view.
146+ * In the examples area there is a demonstration of a sorting algorithm making
147+ * use of exactly this technique to interpose yet another coordinate system
148+ * where the order of the rows is changed, rather than the order of the columns.
149+ * <p>
150+ * Similarly when using the sorting and filtering functionality
151+ * provided by <code>RowSorter</code> the underlying
152+ * <code>TableModel</code> does not need to know how to do sorting,
153+ * rather <code>RowSorter</code> will handle it. Coordinate
154+ * conversions will be necessary when using the row based methods of
155+ * <code>JTable</code> with the underlying <code>TableModel</code>.
156+ * All of <code>JTable</code>s row based methods are in terms of the
157+ * <code>RowSorter</code>, which is not necessarily the same as that
158+ * of the underlying <code>TableModel</code>. For example, the
159+ * selection is always in terms of <code>JTable</code> so that when
160+ * using <code>RowSorter</code> you will need to convert using
161+ * <code>convertRowIndexToView</code> or
162+ * <code>convertRowIndexToModel</code>. The following shows how to
163+ * convert coordinates from <code>JTable</code> to that of the
164+ * underlying model:
165+ * <pre>
166+ * int[] selection = table.getSelectedRows();
167+ * for (int i = 0; i &lt; selection.length; i++) {
168+ * selection[i] = table.convertRowIndexToModel(selection[i]);
169+ * }
170+ * // selection is now in terms of the underlying TableModel
171+ * </pre>
172+ * <p>
173+ * By default if sorting is enabled <code>JTable</code> will persist the
174+ * selection and variable row heights in terms of the model on
175+ * sorting. For example if row 0, in terms of the underlying model,
176+ * is currently selected, after the sort row 0, in terms of the
177+ * underlying model will be selected. Visually the selection may
178+ * change, but in terms of the underlying model it will remain the
179+ * same. The one exception to that is if the model index is no longer
180+ * visible or was removed. For example, if row 0 in terms of model
181+ * was filtered out the selection will be empty after the sort.
182+ * <p>
183+ * J2SE 5 adds methods to <code>JTable</code> to provide convenient access to some
184+ * common printing needs. Simple new {@link #print()} methods allow for quick
185+ * and easy addition of printing support to your application. In addition, a new
186+ * {@link #getPrintable} method is available for more advanced printing needs.
187+ * <p>
188+ * As for all <code>JComponent</code> classes, you can use
189+ * {@link InputMap} and {@link ActionMap} to associate an
190+ * {@link Action} object with a {@link KeyStroke} and execute the
191+ * action under specified conditions.
192+ * <p>
193+ * <strong>Warning:</strong> Swing is not thread safe. For more
194+ * information see <a
195+ * href="package-summary.html#threading">Swing's Threading
196+ * Policy</a>.
197+ * <p>
198+ * <strong>Warning:</strong>
199+ * Serialized objects of this class will not be compatible with
200+ * future Swing releases. The current serialization support is
201+ * appropriate for short term storage or RMI between applications running
202+ * the same version of Swing. As of 1.4, support for long term storage
203+ * of all JavaBeans<sup><font size="-2">TM</font></sup>
204+ * has been added to the <code>java.beans</code> package.
205+ * Please see {@link java.beans.XMLEncoder}.
206+ *
207+ *
208+ * @beaninfo
209+ * attribute: isContainer false
210+ * description: A component which displays data in a two dimensional grid.
211+ *
212+ * @author Philip Milne
213+ * @author Shannon Hickey (printing support)
214+ * @see javax.swing.table.DefaultTableModel
215+ * @see javax.swing.table.TableRowSorter
216+ */
217+/* The first versions of the JTable, contained in Swing-0.1 through
218+ * Swing-0.4, were written by Alan Chung.
219+ */
220+public class JTable extends JComponent implements TableModelListener, Scrollable,
221+ TableColumnModelListener, ListSelectionListener, CellEditorListener,
222+ Accessible, RowSorterListener
223+{
224+//
225+// Static Constants
226+//
227+
228+ /**
229+ * @see #getUIClassID
230+ * @see #readObject
231+ */
232+ private static final String uiClassID = "TableUI";
233+
234+ /** Do not adjust column widths automatically; use a horizontal scrollbar instead. */
235+ public static final int AUTO_RESIZE_OFF = 0;
236+
237+ /** When a column is adjusted in the UI, adjust the next column the opposite way. */
238+ public static final int AUTO_RESIZE_NEXT_COLUMN = 1;
239+
240+ /** During UI adjustment, change subsequent columns to preserve the total width;
241+ * this is the default behavior. */
242+ public static final int AUTO_RESIZE_SUBSEQUENT_COLUMNS = 2;
243+
244+ /** During all resize operations, apply adjustments to the last column only. */
245+ public static final int AUTO_RESIZE_LAST_COLUMN = 3;
246+
247+ /** During all resize operations, proportionately resize all columns. */
248+ public static final int AUTO_RESIZE_ALL_COLUMNS = 4;
249+
250+
251+ /**
252+ * Printing modes, used in printing <code>JTable</code>s.
253+ *
254+ * @see #print(JTable.PrintMode, MessageFormat, MessageFormat,
255+ * boolean, PrintRequestAttributeSet, boolean)
256+ * @see #getPrintable
257+ * @since 1.5
258+ */
259+ public enum PrintMode {
260+
261+ /**
262+ * Printing mode that prints the table at its current size,
263+ * spreading both columns and rows across multiple pages if necessary.
264+ */
265+ NORMAL,
266+
267+ /**
268+ * Printing mode that scales the output smaller, if necessary,
269+ * to fit the table's entire width (and thereby all columns) on each page;
270+ * Rows are spread across multiple pages as necessary.
271+ */
272+ FIT_WIDTH
273+ }
274+
275+
276+//
277+// Instance Variables
278+//
279+
280+ /** The <code>TableModel</code> of the table. */
281+ protected TableModel dataModel;
282+
283+ /** The <code>TableColumnModel</code> of the table. */
284+ protected TableColumnModel columnModel;
285+
286+ /** The <code>ListSelectionModel</code> of the table, used to keep track of row selections. */
287+ protected ListSelectionModel selectionModel;
288+
289+ /** The <code>TableHeader</code> working with the table. */
290+ protected JTableHeader tableHeader;
291+
292+ /** The height in pixels of each row in the table. */
293+ protected int rowHeight;
294+
295+ /** The height in pixels of the margin between the cells in each row. */
296+ protected int rowMargin;
297+
298+ /** The color of the grid. */
299+ protected Color gridColor;
300+
301+ /** The table draws horizontal lines between cells if <code>showHorizontalLines</code> is true. */
302+ protected boolean showHorizontalLines;
303+
304+ /** The table draws vertical lines between cells if <code>showVerticalLines</code> is true. */
305+ protected boolean showVerticalLines;
306+
307+ /**
308+ * Determines if the table automatically resizes the
309+ * width of the table's columns to take up the entire width of the
310+ * table, and how it does the resizing.
311+ */
312+ protected int autoResizeMode;
313+
314+ /**
315+ * The table will query the <code>TableModel</code> to build the default
316+ * set of columns if this is true.
317+ */
318+ protected boolean autoCreateColumnsFromModel;
319+
320+ /** Used by the <code>Scrollable</code> interface to determine the initial visible area. */
321+ protected Dimension preferredViewportSize;
322+
323+ /** True if row selection is allowed in this table. */
324+ protected boolean rowSelectionAllowed;
325+
326+ /**
327+ * Obsolete as of Java 2 platform v1.3. Please use the
328+ * <code>rowSelectionAllowed</code> property and the
329+ * <code>columnSelectionAllowed</code> property of the
330+ * <code>columnModel</code> instead. Or use the
331+ * method <code>getCellSelectionEnabled</code>.
332+ */
333+ /*
334+ * If true, both a row selection and a column selection
335+ * can be non-empty at the same time, the selected cells are the
336+ * the cells whose row and column are both selected.
337+ */
338+ protected boolean cellSelectionEnabled;
339+
340+ /** If editing, the <code>Component</code> that is handling the editing. */
341+ transient protected Component editorComp;
342+
343+ /**
344+ * The active cell editor object, that overwrites the screen real estate
345+ * occupied by the current cell and allows the user to change its contents.
346+ * {@code null} if the table isn't currently editing.
347+ */
348+ transient protected TableCellEditor cellEditor;
349+
350+ /** Identifies the column of the cell being edited. */
351+ transient protected int editingColumn;
352+
353+ /** Identifies the row of the cell being edited. */
354+ transient protected int editingRow;
355+
356+ /**
357+ * A table of objects that display the contents of a cell,
358+ * indexed by class as declared in <code>getColumnClass</code>
359+ * in the <code>TableModel</code> interface.
360+ */
361+ transient protected Hashtable defaultRenderersByColumnClass;
362+
363+ /**
364+ * A table of objects that display and edit the contents of a cell,
365+ * indexed by class as declared in <code>getColumnClass</code>
366+ * in the <code>TableModel</code> interface.
367+ */
368+ transient protected Hashtable defaultEditorsByColumnClass;
369+
370+ /** The foreground color of selected cells. */
371+ protected Color selectionForeground;
372+
373+ /** The background color of selected cells. */
374+ protected Color selectionBackground;
375+
376+//
377+// Private state
378+//
379+
380+ // WARNING: If you directly access this field you should also change the
381+ // SortManager.modelRowSizes field as well.
382+ private SizeSequence rowModel;
383+ private boolean dragEnabled;
384+ private boolean surrendersFocusOnKeystroke;
385+ private PropertyChangeListener editorRemover = null;
386+ /**
387+ * The last value of getValueIsAdjusting from the column selection models
388+ * columnSelectionChanged notification. Used to test if a repaint is
389+ * needed.
390+ */
391+ private boolean columnSelectionAdjusting;
392+ /**
393+ * The last value of getValueIsAdjusting from the row selection models
394+ * valueChanged notification. Used to test if a repaint is needed.
395+ */
396+ private boolean rowSelectionAdjusting;
397+
398+ /**
399+ * To communicate errors between threads during printing.
400+ */
401+ private Throwable printError;
402+
403+ /**
404+ * True when setRowHeight(int) has been invoked.
405+ */
406+ private boolean isRowHeightSet;
407+
408+ /**
409+ * If true, on a sort the selection is reset.
410+ */
411+ private boolean updateSelectionOnSort;
412+
413+ /**
414+ * Information used in sorting.
415+ */
416+ private transient SortManager sortManager;
417+
418+ /**
419+ * If true, when sorterChanged is invoked it's value is ignored.
420+ */
421+ private boolean ignoreSortChange;
422+
423+ /**
424+ * Whether or not sorterChanged has been invoked.
425+ */
426+ private boolean sorterChanged;
427+
428+ /**
429+ * If true, any time the model changes a new RowSorter is set.
430+ */
431+ private boolean autoCreateRowSorter;
432+
433+ /**
434+ * Whether or not the table always fills the viewport height.
435+ * @see #setFillsViewportHeight
436+ * @see #getScrollableTracksViewportHeight
437+ */
438+ private boolean fillsViewportHeight;
439+
440+ /**
441+ * The drop mode for this component.
442+ */
443+ private DropMode dropMode = DropMode.USE_SELECTION;
444+
445+ /**
446+ * The drop location.
447+ */
448+ private transient DropLocation dropLocation;
449+
450+ /**
451+ * A subclass of <code>TransferHandler.DropLocation</code> representing
452+ * a drop location for a <code>JTable</code>.
453+ *
454+ * @see #getDropLocation
455+ * @since 1.6
456+ */
457+ public static final class DropLocation extends TransferHandler.DropLocation {
458+ private final int row;
459+ private final int col;
460+ private final boolean isInsertRow;
461+ private final boolean isInsertCol;
462+
463+ private DropLocation(Point p, int row, int col,
464+ boolean isInsertRow, boolean isInsertCol) {
465+
466+ super(p);
467+ this.row = row;
468+ this.col = col;
469+ this.isInsertRow = isInsertRow;
470+ this.isInsertCol = isInsertCol;
471+ }
472+
473+ /**
474+ * Returns the row index where a dropped item should be placed in the
475+ * table. Interpretation of the value depends on the return of
476+ * <code>isInsertRow()</code>. If that method returns
477+ * <code>true</code> this value indicates the index where a new
478+ * row should be inserted. Otherwise, it represents the value
479+ * of an existing row on which the data was dropped. This index is
480+ * in terms of the view.
481+ * <p>
482+ * <code>-1</code> indicates that the drop occurred over empty space,
483+ * and no row could be calculated.
484+ *
485+ * @return the drop row
486+ */
487+ public int getRow() {
488+ return row;
489+ }
490+
491+ /**
492+ * Returns the column index where a dropped item should be placed in the
493+ * table. Interpretation of the value depends on the return of
494+ * <code>isInsertColumn()</code>. If that method returns
495+ * <code>true</code> this value indicates the index where a new
496+ * column should be inserted. Otherwise, it represents the value
497+ * of an existing column on which the data was dropped. This index is
498+ * in terms of the view.
499+ * <p>
500+ * <code>-1</code> indicates that the drop occurred over empty space,
501+ * and no column could be calculated.
502+ *
503+ * @return the drop row
504+ */
505+ public int getColumn() {
506+ return col;
507+ }
508+
509+ /**
510+ * Returns whether or not this location represents an insert
511+ * of a row.
512+ *
513+ * @return whether or not this is an insert row
514+ */
515+ public boolean isInsertRow() {
516+ return isInsertRow;
517+ }
518+
519+ /**
520+ * Returns whether or not this location represents an insert
521+ * of a column.
522+ *
523+ * @return whether or not this is an insert column
524+ */
525+ public boolean isInsertColumn() {
526+ return isInsertCol;
527+ }
528+
529+ /**
530+ * Returns a string representation of this drop location.
531+ * This method is intended to be used for debugging purposes,
532+ * and the content and format of the returned string may vary
533+ * between implementations.
534+ *
535+ * @return a string representation of this drop location
536+ */
537+ public String toString() {
538+ return getClass().getName()
539+ + "[dropPoint=" + getDropPoint() + ","
540+ + "row=" + row + ","
541+ + "column=" + col + ","
542+ + "insertRow=" + isInsertRow + ","
543+ + "insertColumn=" + isInsertCol + "]";
544+ }
545+ }
546+
547+//
548+// Constructors
549+//
550+
551+ /**
552+ * Constructs a default <code>JTable</code> that is initialized with a default
553+ * data model, a default column model, and a default selection
554+ * model.
555+ *
556+ * @see #createDefaultDataModel
557+ * @see #createDefaultColumnModel
558+ * @see #createDefaultSelectionModel
559+ */
560+ public JTable() {
561+ this(null, null, null);
562+ }
563+
564+ /**
565+ * Constructs a <code>JTable</code> that is initialized with
566+ * <code>dm</code> as the data model, a default column model,
567+ * and a default selection model.
568+ *
569+ * @param dm the data model for the table
570+ * @see #createDefaultColumnModel
571+ * @see #createDefaultSelectionModel
572+ */
573+ public JTable(TableModel dm) {
574+ this(dm, null, null);
575+ }
576+
577+ /**
578+ * Constructs a <code>JTable</code> that is initialized with
579+ * <code>dm</code> as the data model, <code>cm</code>
580+ * as the column model, and a default selection model.
581+ *
582+ * @param dm the data model for the table
583+ * @param cm the column model for the table
584+ * @see #createDefaultSelectionModel
585+ */
586+ public JTable(TableModel dm, TableColumnModel cm) {
587+ this(dm, cm, null);
588+ }
589+
590+ /**
591+ * Constructs a <code>JTable</code> that is initialized with
592+ * <code>dm</code> as the data model, <code>cm</code> as the
593+ * column model, and <code>sm</code> as the selection model.
594+ * If any of the parameters are <code>null</code> this method
595+ * will initialize the table with the corresponding default model.
596+ * The <code>autoCreateColumnsFromModel</code> flag is set to false
597+ * if <code>cm</code> is non-null, otherwise it is set to true
598+ * and the column model is populated with suitable
599+ * <code>TableColumns</code> for the columns in <code>dm</code>.
600+ *
601+ * @param dm the data model for the table
602+ * @param cm the column model for the table
603+ * @param sm the row selection model for the table
604+ * @see #createDefaultDataModel
605+ * @see #createDefaultColumnModel
606+ * @see #createDefaultSelectionModel
607+ */
608+ public JTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) {
609+ super();
610+ setLayout(null);
611+
612+ setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
613+ JComponent.getManagingFocusForwardTraversalKeys());
614+ setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
615+ JComponent.getManagingFocusBackwardTraversalKeys());
616+ if (cm == null) {
617+ cm = createDefaultColumnModel();
618+ autoCreateColumnsFromModel = true;
619+ }
620+ setColumnModel(cm);
621+
622+ if (sm == null) {
623+ sm = createDefaultSelectionModel();
624+ }
625+ setSelectionModel(sm);
626+
627+ // Set the model last, that way if the autoCreatColumnsFromModel has
628+ // been set above, we will automatically populate an empty columnModel
629+ // with suitable columns for the new model.
630+ if (dm == null) {
631+ dm = createDefaultDataModel();
632+ }
633+ setModel(dm);
634+
635+ initializeLocalVars();
636+ updateUI();
637+ }
638+
639+ /**
640+ * Constructs a <code>JTable</code> with <code>numRows</code>
641+ * and <code>numColumns</code> of empty cells using
642+ * <code>DefaultTableModel</code>. The columns will have
643+ * names of the form "A", "B", "C", etc.
644+ *
645+ * @param numRows the number of rows the table holds
646+ * @param numColumns the number of columns the table holds
647+ * @see javax.swing.table.DefaultTableModel
648+ */
649+ public JTable(int numRows, int numColumns) {
650+ this(new DefaultTableModel(numRows, numColumns));
651+ }
652+
653+ /**
654+ * Constructs a <code>JTable</code> to display the values in the
655+ * <code>Vector</code> of <code>Vectors</code>, <code>rowData</code>,
656+ * with column names, <code>columnNames</code>. The
657+ * <code>Vectors</code> contained in <code>rowData</code>
658+ * should contain the values for that row. In other words,
659+ * the value of the cell at row 1, column 5 can be obtained
660+ * with the following code:
661+ * <p>
662+ * <pre>((Vector)rowData.elementAt(1)).elementAt(5);</pre>
663+ * <p>
664+ * @param rowData the data for the new table
665+ * @param columnNames names of each column
666+ */
667+ public JTable(Vector rowData, Vector columnNames) {
668+ this(new DefaultTableModel(rowData, columnNames));
669+ }
670+
671+ /**
672+ * Constructs a <code>JTable</code> to display the values in the two dimensional array,
673+ * <code>rowData</code>, with column names, <code>columnNames</code>.
674+ * <code>rowData</code> is an array of rows, so the value of the cell at row 1,
675+ * column 5 can be obtained with the following code:
676+ * <p>
677+ * <pre> rowData[1][5]; </pre>
678+ * <p>
679+ * All rows must be of the same length as <code>columnNames</code>.
680+ * <p>
681+ * @param rowData the data for the new table
682+ * @param columnNames names of each column
683+ */
684+ public JTable(final Object[][] rowData, final Object[] columnNames) {
685+ this(new AbstractTableModel() {
686+ public String getColumnName(int column) { return columnNames[column].toString(); }
687+ public int getRowCount() { return rowData.length; }
688+ public int getColumnCount() { return columnNames.length; }
689+ public Object getValueAt(int row, int col) { return rowData[row][col]; }
690+ public boolean isCellEditable(int row, int column) { return true; }
691+ public void setValueAt(Object value, int row, int col) {
692+ rowData[row][col] = value;
693+ fireTableCellUpdated(row, col);
694+ }
695+ });
696+ }
697+
698+ /**
699+ * Calls the <code>configureEnclosingScrollPane</code> method.
700+ *
701+ * @see #configureEnclosingScrollPane
702+ */
703+ public void addNotify() {
704+ super.addNotify();
705+ configureEnclosingScrollPane();
706+ }
707+
708+ /**
709+ * If this <code>JTable</code> is the <code>viewportView</code> of an enclosing <code>JScrollPane</code>
710+ * (the usual situation), configure this <code>ScrollPane</code> by, amongst other things,
711+ * installing the table's <code>tableHeader</code> as the <code>columnHeaderView</code> of the scroll pane.
712+ * When a <code>JTable</code> is added to a <code>JScrollPane</code> in the usual way,
713+ * using <code>new JScrollPane(myTable)</code>, <code>addNotify</code> is
714+ * called in the <code>JTable</code> (when the table is added to the viewport).
715+ * <code>JTable</code>'s <code>addNotify</code> method in turn calls this method,
716+ * which is protected so that this default installation procedure can
717+ * be overridden by a subclass.
718+ *
719+ * @see #addNotify
720+ */
721+ protected void configureEnclosingScrollPane() {
722+ Container parent = SwingUtilities.getUnwrappedParent(this);
723+ if (parent instanceof JViewport) {
724+ JViewport port = (JViewport) parent;
725+ Container gp = port.getParent();
726+ if (gp instanceof JScrollPane) {
727+ JScrollPane scrollPane = (JScrollPane)gp;
728+ // Make certain we are the viewPort's view and not, for
729+ // example, the rowHeaderView of the scrollPane -
730+ // an implementor of fixed columns might do this.
731+ JViewport viewport = scrollPane.getViewport();
732+ if (viewport == null ||
733+ SwingUtilities.getUnwrappedView(viewport) != this) {
734+ return;
735+ }
736+ scrollPane.setColumnHeaderView(getTableHeader());
737+ // configure the scrollpane for any LAF dependent settings
738+ configureEnclosingScrollPaneUI();
739+ }
740+ }
741+ }
742+
743+ /**
744+ * This is a sub-part of configureEnclosingScrollPane() that configures
745+ * anything on the scrollpane that may change when the look and feel
746+ * changes. It needed to be split out from configureEnclosingScrollPane() so
747+ * that it can be called from updateUI() when the LAF changes without
748+ * causing the regression found in bug 6687962. This was because updateUI()
749+ * is called from the constructor which then caused
750+ * configureEnclosingScrollPane() to be called by the constructor which
751+ * changes its contract for any subclass that overrides it. So by splitting
752+ * it out in this way configureEnclosingScrollPaneUI() can be called both
753+ * from configureEnclosingScrollPane() and updateUI() in a safe manor.
754+ */
755+ private void configureEnclosingScrollPaneUI() {
756+ Container parent = SwingUtilities.getUnwrappedParent(this);
757+ if (parent instanceof JViewport) {
758+ JViewport port = (JViewport) parent;
759+ Container gp = port.getParent();
760+ if (gp instanceof JScrollPane) {
761+ JScrollPane scrollPane = (JScrollPane)gp;
762+ // Make certain we are the viewPort's view and not, for
763+ // example, the rowHeaderView of the scrollPane -
764+ // an implementor of fixed columns might do this.
765+ JViewport viewport = scrollPane.getViewport();
766+ if (viewport == null ||
767+ SwingUtilities.getUnwrappedView(viewport) != this) {
768+ return;
769+ }
770+ // scrollPane.getViewport().setBackingStoreEnabled(true);
771+ Border border = scrollPane.getBorder();
772+ if (border == null || border instanceof UIResource) {
773+ Border scrollPaneBorder =
774+ UIManager.getBorder("Table.scrollPaneBorder");
775+ if (scrollPaneBorder != null) {
776+ scrollPane.setBorder(scrollPaneBorder);
777+ }
778+ }
779+ // add JScrollBar corner component if available from LAF and not already set by the user
780+ Component corner =
781+ scrollPane.getCorner(JScrollPane.UPPER_TRAILING_CORNER);
782+ if (corner == null || corner instanceof UIResource){
783+ corner = null;
784+ Object componentClass = UIManager.get(
785+ "Table.scrollPaneCornerComponent");
786+ if (componentClass instanceof Class){
787+ try {
788+ corner = (Component)
789+ ((Class)componentClass).newInstance();
790+ } catch (Exception e) {
791+ // just ignore and don't set corner
792+ }
793+ }
794+ scrollPane.setCorner(JScrollPane.UPPER_TRAILING_CORNER,
795+ corner);
796+ }
797+ }
798+ }
799+ }
800+
801+ /**
802+ * Calls the <code>unconfigureEnclosingScrollPane</code> method.
803+ *
804+ * @see #unconfigureEnclosingScrollPane
805+ */
806+ public void removeNotify() {
807+ KeyboardFocusManager.getCurrentKeyboardFocusManager().
808+ removePropertyChangeListener("permanentFocusOwner", editorRemover);
809+ editorRemover = null;
810+ unconfigureEnclosingScrollPane();
811+ super.removeNotify();
812+ }
813+
814+ /**
815+ * Reverses the effect of <code>configureEnclosingScrollPane</code>
816+ * by replacing the <code>columnHeaderView</code> of the enclosing
817+ * scroll pane with <code>null</code>. <code>JTable</code>'s
818+ * <code>removeNotify</code> method calls
819+ * this method, which is protected so that this default uninstallation
820+ * procedure can be overridden by a subclass.
821+ *
822+ * @see #removeNotify
823+ * @see #configureEnclosingScrollPane
824+ * @since 1.3
825+ */
826+ protected void unconfigureEnclosingScrollPane() {
827+ Container parent = SwingUtilities.getUnwrappedParent(this);
828+ if (parent instanceof JViewport) {
829+ JViewport port = (JViewport) parent;
830+ Container gp = port.getParent();
831+ if (gp instanceof JScrollPane) {
832+ JScrollPane scrollPane = (JScrollPane)gp;
833+ // Make certain we are the viewPort's view and not, for
834+ // example, the rowHeaderView of the scrollPane -
835+ // an implementor of fixed columns might do this.
836+ JViewport viewport = scrollPane.getViewport();
837+ if (viewport == null ||
838+ SwingUtilities.getUnwrappedView(viewport) != this) {
839+ return;
840+ }
841+ scrollPane.setColumnHeaderView(null);
842+ // remove ScrollPane corner if one was added by the LAF
843+ Component corner =
844+ scrollPane.getCorner(JScrollPane.UPPER_TRAILING_CORNER);
845+ if (corner instanceof UIResource){
846+ scrollPane.setCorner(JScrollPane.UPPER_TRAILING_CORNER,
847+ null);
848+ }
849+ }
850+ }
851+ }
852+
853+ void setUIProperty(String propertyName, Object value) {
854+ if (propertyName == "rowHeight") {
855+ if (!isRowHeightSet) {
856+ setRowHeight(((Number)value).intValue());
857+ isRowHeightSet = false;
858+ }
859+ return;
860+ }
861+ super.setUIProperty(propertyName, value);
862+ }
863+
864+//
865+// Static Methods
866+//
867+
868+ /**
869+ * Equivalent to <code>new JScrollPane(aTable)</code>.
870+ *
871+ * @deprecated As of Swing version 1.0.2,
872+ * replaced by <code>new JScrollPane(aTable)</code>.
873+ */
874+ @Deprecated
875+ static public JScrollPane createScrollPaneForTable(JTable aTable) {
876+ return new JScrollPane(aTable);
877+ }
878+
879+//
880+// Table Attributes
881+//
882+
883+ /**
884+ * Sets the <code>tableHeader</code> working with this <code>JTable</code> to <code>newHeader</code>.
885+ * It is legal to have a <code>null</code> <code>tableHeader</code>.
886+ *
887+ * @param tableHeader new tableHeader
888+ * @see #getTableHeader
889+ * @beaninfo
890+ * bound: true
891+ * description: The JTableHeader instance which renders the column headers.
892+ */
893+ public void setTableHeader(JTableHeader tableHeader) {
894+ if (this.tableHeader != tableHeader) {
895+ JTableHeader old = this.tableHeader;
896+ // Release the old header
897+ if (old != null) {
898+ old.setTable(null);
899+ }
900+ this.tableHeader = tableHeader;
901+ if (tableHeader != null) {
902+ tableHeader.setTable(this);
903+ }
904+ firePropertyChange("tableHeader", old, tableHeader);
905+ }
906+ }
907+
908+ /**
909+ * Returns the <code>tableHeader</code> used by this <code>JTable</code>.
910+ *
911+ * @return the <code>tableHeader</code> used by this table
912+ * @see #setTableHeader
913+ */
914+ public JTableHeader getTableHeader() {
915+ return tableHeader;
916+ }
917+
918+ /**
919+ * Sets the height, in pixels, of all cells to <code>rowHeight</code>,
920+ * revalidates, and repaints.
921+ * The height of the cells will be equal to the row height minus
922+ * the row margin.
923+ *
924+ * @param rowHeight new row height
925+ * @exception IllegalArgumentException if <code>rowHeight</code> is
926+ * less than 1
927+ * @see #getRowHeight
928+ * @beaninfo
929+ * bound: true
930+ * description: The height of the specified row.
931+ */
932+ public void setRowHeight(int rowHeight) {
933+ if (rowHeight <= 0) {
934+ throw new IllegalArgumentException("New row height less than 1");
935+ }
936+ int old = this.rowHeight;
937+ this.rowHeight = rowHeight;
938+ rowModel = null;
939+ if (sortManager != null) {
940+ sortManager.modelRowSizes = null;
941+ }
942+ isRowHeightSet = true;
943+ resizeAndRepaint();
944+ firePropertyChange("rowHeight", old, rowHeight);
945+ }
946+
947+ /**
948+ * Returns the height of a table row, in pixels.
949+ *
950+ * @return the height in pixels of a table row
951+ * @see #setRowHeight
952+ */
953+ public int getRowHeight() {
954+ return rowHeight;
955+ }
956+
957+ private SizeSequence getRowModel() {
958+ if (rowModel == null) {
959+ rowModel = new SizeSequence(getRowCount(), getRowHeight());
960+ }
961+ return rowModel;
962+ }
963+
964+ /**
965+ * Sets the height for <code>row</code> to <code>rowHeight</code>,
966+ * revalidates, and repaints. The height of the cells in this row
967+ * will be equal to the row height minus the row margin.
968+ *
969+ * @param row the row whose height is being
970+ changed
971+ * @param rowHeight new row height, in pixels
972+ * @exception IllegalArgumentException if <code>rowHeight</code> is
973+ * less than 1
974+ * @beaninfo
975+ * bound: true
976+ * description: The height in pixels of the cells in <code>row</code>
977+ * @since 1.3
978+ */
979+ public void setRowHeight(int row, int rowHeight) {
980+ if (rowHeight <= 0) {
981+ throw new IllegalArgumentException("New row height less than 1");
982+ }
983+ getRowModel().setSize(row, rowHeight);
984+ if (sortManager != null) {
985+ sortManager.setViewRowHeight(row, rowHeight);
986+ }
987+ resizeAndRepaint();
988+ }
989+
990+ /**
991+ * Returns the height, in pixels, of the cells in <code>row</code>.
992+ * @param row the row whose height is to be returned
993+ * @return the height, in pixels, of the cells in the row
994+ * @since 1.3
995+ */
996+ public int getRowHeight(int row) {
997+ return (rowModel == null) ? getRowHeight() : rowModel.getSize(row);
998+ }
999+
1000+ /**
1001+ * Sets the amount of empty space between cells in adjacent rows.
1002+ *
1003+ * @param rowMargin the number of pixels between cells in a row
1004+ * @see #getRowMargin
1005+ * @beaninfo
1006+ * bound: true
1007+ * description: The amount of space between cells.
1008+ */
1009+ public void setRowMargin(int rowMargin) {
1010+ int old = this.rowMargin;
1011+ this.rowMargin = rowMargin;
1012+ resizeAndRepaint();
1013+ firePropertyChange("rowMargin", old, rowMargin);
1014+ }
1015+
1016+ /**
1017+ * Gets the amount of empty space, in pixels, between cells. Equivalent to:
1018+ * <code>getIntercellSpacing().height</code>.
1019+ * @return the number of pixels between cells in a row
1020+ *
1021+ * @see #setRowMargin
1022+ */
1023+ public int getRowMargin() {
1024+ return rowMargin;
1025+ }
1026+
1027+ /**
1028+ * Sets the <code>rowMargin</code> and the <code>columnMargin</code> --
1029+ * the height and width of the space between cells -- to
1030+ * <code>intercellSpacing</code>.
1031+ *
1032+ * @param intercellSpacing a <code>Dimension</code>
1033+ * specifying the new width
1034+ * and height between cells
1035+ * @see #getIntercellSpacing
1036+ * @beaninfo
1037+ * description: The spacing between the cells,
1038+ * drawn in the background color of the JTable.
1039+ */
1040+ public void setIntercellSpacing(Dimension intercellSpacing) {
1041+ // Set the rowMargin here and columnMargin in the TableColumnModel
1042+ setRowMargin(intercellSpacing.height);
1043+ getColumnModel().setColumnMargin(intercellSpacing.width);
1044+
1045+ resizeAndRepaint();
1046+ }
1047+
1048+ /**
1049+ * Returns the horizontal and vertical space between cells.
1050+ * The default spacing is look and feel dependent.
1051+ *
1052+ * @return the horizontal and vertical spacing between cells
1053+ * @see #setIntercellSpacing
1054+ */
1055+ public Dimension getIntercellSpacing() {
1056+ return new Dimension(getColumnModel().getColumnMargin(), rowMargin);
1057+ }
1058+
1059+ /**
1060+ * Sets the color used to draw grid lines to <code>gridColor</code> and redisplays.
1061+ * The default color is look and feel dependent.
1062+ *
1063+ * @param gridColor the new color of the grid lines
1064+ * @exception IllegalArgumentException if <code>gridColor</code> is <code>null</code>
1065+ * @see #getGridColor
1066+ * @beaninfo
1067+ * bound: true
1068+ * description: The grid color.
1069+ */
1070+ public void setGridColor(Color gridColor) {
1071+ if (gridColor == null) {
1072+ throw new IllegalArgumentException("New color is null");
1073+ }
1074+ Color old = this.gridColor;
1075+ this.gridColor = gridColor;
1076+ firePropertyChange("gridColor", old, gridColor);
1077+ // Redraw
1078+ repaint();
1079+ }
1080+
1081+ /**
1082+ * Returns the color used to draw grid lines.
1083+ * The default color is look and feel dependent.
1084+ *
1085+ * @return the color used to draw grid lines
1086+ * @see #setGridColor
1087+ */
1088+ public Color getGridColor() {
1089+ return gridColor;
1090+ }
1091+
1092+ /**
1093+ * Sets whether the table draws grid lines around cells.
1094+ * If <code>showGrid</code> is true it does; if it is false it doesn't.
1095+ * There is no <code>getShowGrid</code> method as this state is held
1096+ * in two variables -- <code>showHorizontalLines</code> and <code>showVerticalLines</code> --
1097+ * each of which can be queried independently.
1098+ *
1099+ * @param showGrid true if table view should draw grid lines
1100+ *
1101+ * @see #setShowVerticalLines
1102+ * @see #setShowHorizontalLines
1103+ * @beaninfo
1104+ * description: The color used to draw the grid lines.
1105+ */
1106+ public void setShowGrid(boolean showGrid) {
1107+ setShowHorizontalLines(showGrid);
1108+ setShowVerticalLines(showGrid);
1109+
1110+ // Redraw
1111+ repaint();
1112+ }
1113+
1114+ /**
1115+ * Sets whether the table draws horizontal lines between cells.
1116+ * If <code>showHorizontalLines</code> is true it does; if it is false it doesn't.
1117+ *
1118+ * @param showHorizontalLines true if table view should draw horizontal lines
1119+ * @see #getShowHorizontalLines
1120+ * @see #setShowGrid
1121+ * @see #setShowVerticalLines
1122+ * @beaninfo
1123+ * bound: true
1124+ * description: Whether horizontal lines should be drawn in between the cells.
1125+ */
1126+ public void setShowHorizontalLines(boolean showHorizontalLines) {
1127+ boolean old = this.showHorizontalLines;
1128+ this.showHorizontalLines = showHorizontalLines;
1129+ firePropertyChange("showHorizontalLines", old, showHorizontalLines);
1130+
1131+ // Redraw
1132+ repaint();
1133+ }
1134+
1135+ /**
1136+ * Sets whether the table draws vertical lines between cells.
1137+ * If <code>showVerticalLines</code> is true it does; if it is false it doesn't.
1138+ *
1139+ * @param showVerticalLines true if table view should draw vertical lines
1140+ * @see #getShowVerticalLines
1141+ * @see #setShowGrid
1142+ * @see #setShowHorizontalLines
1143+ * @beaninfo
1144+ * bound: true
1145+ * description: Whether vertical lines should be drawn in between the cells.
1146+ */
1147+ public void setShowVerticalLines(boolean showVerticalLines) {
1148+ boolean old = this.showVerticalLines;
1149+ this.showVerticalLines = showVerticalLines;
1150+ firePropertyChange("showVerticalLines", old, showVerticalLines);
1151+ // Redraw
1152+ repaint();
1153+ }
1154+
1155+ /**
1156+ * Returns true if the table draws horizontal lines between cells, false if it
1157+ * doesn't. The default value is look and feel dependent.
1158+ *
1159+ * @return true if the table draws horizontal lines between cells, false if it
1160+ * doesn't
1161+ * @see #setShowHorizontalLines
1162+ */
1163+ public boolean getShowHorizontalLines() {
1164+ return showHorizontalLines;
1165+ }
1166+
1167+ /**
1168+ * Returns true if the table draws vertical lines between cells, false if it
1169+ * doesn't. The default value is look and feel dependent.
1170+ *
1171+ * @return true if the table draws vertical lines between cells, false if it
1172+ * doesn't
1173+ * @see #setShowVerticalLines
1174+ */
1175+ public boolean getShowVerticalLines() {
1176+ return showVerticalLines;
1177+ }
1178+
1179+ /**
1180+ * Sets the table's auto resize mode when the table is resized. For further
1181+ * information on how the different resize modes work, see
1182+ * {@link #doLayout}.
1183+ *
1184+ * @param mode One of 5 legal values:
1185+ * AUTO_RESIZE_OFF,
1186+ * AUTO_RESIZE_NEXT_COLUMN,
1187+ * AUTO_RESIZE_SUBSEQUENT_COLUMNS,
1188+ * AUTO_RESIZE_LAST_COLUMN,
1189+ * AUTO_RESIZE_ALL_COLUMNS
1190+ *
1191+ * @see #getAutoResizeMode
1192+ * @see #doLayout
1193+ * @beaninfo
1194+ * bound: true
1195+ * description: Whether the columns should adjust themselves automatically.
1196+ * enum: AUTO_RESIZE_OFF JTable.AUTO_RESIZE_OFF
1197+ * AUTO_RESIZE_NEXT_COLUMN JTable.AUTO_RESIZE_NEXT_COLUMN
1198+ * AUTO_RESIZE_SUBSEQUENT_COLUMNS JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS
1199+ * AUTO_RESIZE_LAST_COLUMN JTable.AUTO_RESIZE_LAST_COLUMN
1200+ * AUTO_RESIZE_ALL_COLUMNS JTable.AUTO_RESIZE_ALL_COLUMNS
1201+ */
1202+ public void setAutoResizeMode(int mode) {
1203+ if ((mode == AUTO_RESIZE_OFF) ||
1204+ (mode == AUTO_RESIZE_NEXT_COLUMN) ||
1205+ (mode == AUTO_RESIZE_SUBSEQUENT_COLUMNS) ||
1206+ (mode == AUTO_RESIZE_LAST_COLUMN) ||
1207+ (mode == AUTO_RESIZE_ALL_COLUMNS)) {
1208+ int old = autoResizeMode;
1209+ autoResizeMode = mode;
1210+ resizeAndRepaint();
1211+ if (tableHeader != null) {
1212+ tableHeader.resizeAndRepaint();
1213+ }
1214+ firePropertyChange("autoResizeMode", old, autoResizeMode);
1215+ }
1216+ }
1217+
1218+ /**
1219+ * Returns the auto resize mode of the table. The default mode
1220+ * is AUTO_RESIZE_SUBSEQUENT_COLUMNS.
1221+ *
1222+ * @return the autoResizeMode of the table
1223+ *
1224+ * @see #setAutoResizeMode
1225+ * @see #doLayout
1226+ */
1227+ public int getAutoResizeMode() {
1228+ return autoResizeMode;
1229+ }
1230+
1231+ /**
1232+ * Sets this table's <code>autoCreateColumnsFromModel</code> flag.
1233+ * This method calls <code>createDefaultColumnsFromModel</code> if
1234+ * <code>autoCreateColumnsFromModel</code> changes from false to true.
1235+ *
1236+ * @param autoCreateColumnsFromModel true if <code>JTable</code> should automatically create columns
1237+ * @see #getAutoCreateColumnsFromModel
1238+ * @see #createDefaultColumnsFromModel
1239+ * @beaninfo
1240+ * bound: true
1241+ * description: Automatically populates the columnModel when a new TableModel is submitted.
1242+ */
1243+ public void setAutoCreateColumnsFromModel(boolean autoCreateColumnsFromModel) {
1244+ if (this.autoCreateColumnsFromModel != autoCreateColumnsFromModel) {
1245+ boolean old = this.autoCreateColumnsFromModel;
1246+ this.autoCreateColumnsFromModel = autoCreateColumnsFromModel;
1247+ if (autoCreateColumnsFromModel) {
1248+ createDefaultColumnsFromModel();
1249+ }
1250+ firePropertyChange("autoCreateColumnsFromModel", old, autoCreateColumnsFromModel);
1251+ }
1252+ }
1253+
1254+ /**
1255+ * Determines whether the table will create default columns from the model.
1256+ * If true, <code>setModel</code> will clear any existing columns and
1257+ * create new columns from the new model. Also, if the event in
1258+ * the <code>tableChanged</code> notification specifies that the
1259+ * entire table changed, then the columns will be rebuilt.
1260+ * The default is true.
1261+ *
1262+ * @return the autoCreateColumnsFromModel of the table
1263+ * @see #setAutoCreateColumnsFromModel
1264+ * @see #createDefaultColumnsFromModel
1265+ */
1266+ public boolean getAutoCreateColumnsFromModel() {
1267+ return autoCreateColumnsFromModel;
1268+ }
1269+
1270+ /**
1271+ * Creates default columns for the table from
1272+ * the data model using the <code>getColumnCount</code> method
1273+ * defined in the <code>TableModel</code> interface.
1274+ * <p>
1275+ * Clears any existing columns before creating the
1276+ * new columns based on information from the model.
1277+ *
1278+ * @see #getAutoCreateColumnsFromModel
1279+ */
1280+ public void createDefaultColumnsFromModel() {
1281+ TableModel m = getModel();
1282+ if (m != null) {
1283+ // Remove any current columns
1284+ TableColumnModel cm = getColumnModel();
1285+ while (cm.getColumnCount() > 0) {
1286+ cm.removeColumn(cm.getColumn(0));
1287+ }
1288+
1289+ // Create new columns from the data model info
1290+ for (int i = 0; i < m.getColumnCount(); i++) {
1291+ TableColumn newColumn = new TableColumn(i);
1292+ addColumn(newColumn);
1293+ }
1294+ }
1295+ }
1296+
1297+ /**
1298+ * Sets a default cell renderer to be used if no renderer has been set in
1299+ * a <code>TableColumn</code>. If renderer is <code>null</code>,
1300+ * removes the default renderer for this column class.
1301+ *
1302+ * @param columnClass set the default cell renderer for this columnClass
1303+ * @param renderer default cell renderer to be used for this
1304+ * columnClass
1305+ * @see #getDefaultRenderer
1306+ * @see #setDefaultEditor
1307+ */
1308+ public void setDefaultRenderer(Class<?> columnClass, TableCellRenderer renderer) {
1309+ if (renderer != null) {
1310+ defaultRenderersByColumnClass.put(columnClass, renderer);
1311+ }
1312+ else {
1313+ defaultRenderersByColumnClass.remove(columnClass);
1314+ }
1315+ }
1316+
1317+ /**
1318+ * Returns the cell renderer to be used when no renderer has been set in
1319+ * a <code>TableColumn</code>. During the rendering of cells the renderer is fetched from
1320+ * a <code>Hashtable</code> of entries according to the class of the cells in the column. If
1321+ * there is no entry for this <code>columnClass</code> the method returns
1322+ * the entry for the most specific superclass. The <code>JTable</code> installs entries
1323+ * for <code>Object</code>, <code>Number</code>, and <code>Boolean</code>, all of which can be modified
1324+ * or replaced.
1325+ *
1326+ * @param columnClass return the default cell renderer
1327+ * for this columnClass
1328+ * @return the renderer for this columnClass
1329+ * @see #setDefaultRenderer
1330+ * @see #getColumnClass
1331+ */
1332+ public TableCellRenderer getDefaultRenderer(Class<?> columnClass) {
1333+ if (columnClass == null) {
1334+ return null;
1335+ }
1336+ else {
1337+ Object renderer = defaultRenderersByColumnClass.get(columnClass);
1338+ if (renderer != null) {
1339+ return (TableCellRenderer)renderer;
1340+ }
1341+ else {
1342+ Class c = columnClass.getSuperclass();
1343+ if (c == null && columnClass != Object.class) {
1344+ c = Object.class;
1345+ }
1346+ return getDefaultRenderer(c);
1347+ }
1348+ }
1349+ }
1350+
1351+ /**
1352+ * Sets a default cell editor to be used if no editor has been set in
1353+ * a <code>TableColumn</code>. If no editing is required in a table, or a
1354+ * particular column in a table, uses the <code>isCellEditable</code>
1355+ * method in the <code>TableModel</code> interface to ensure that this
1356+ * <code>JTable</code> will not start an editor in these columns.
1357+ * If editor is <code>null</code>, removes the default editor for this
1358+ * column class.
1359+ *
1360+ * @param columnClass set the default cell editor for this columnClass
1361+ * @param editor default cell editor to be used for this columnClass
1362+ * @see TableModel#isCellEditable
1363+ * @see #getDefaultEditor
1364+ * @see #setDefaultRenderer
1365+ */
1366+ public void setDefaultEditor(Class<?> columnClass, TableCellEditor editor) {
1367+ if (editor != null) {
1368+ defaultEditorsByColumnClass.put(columnClass, editor);
1369+ }
1370+ else {
1371+ defaultEditorsByColumnClass.remove(columnClass);
1372+ }
1373+ }
1374+
1375+ /**
1376+ * Returns the editor to be used when no editor has been set in
1377+ * a <code>TableColumn</code>. During the editing of cells the editor is fetched from
1378+ * a <code>Hashtable</code> of entries according to the class of the cells in the column. If
1379+ * there is no entry for this <code>columnClass</code> the method returns
1380+ * the entry for the most specific superclass. The <code>JTable</code> installs entries
1381+ * for <code>Object</code>, <code>Number</code>, and <code>Boolean</code>, all of which can be modified
1382+ * or replaced.
1383+ *
1384+ * @param columnClass return the default cell editor for this columnClass
1385+ * @return the default cell editor to be used for this columnClass
1386+ * @see #setDefaultEditor
1387+ * @see #getColumnClass
1388+ */
1389+ public TableCellEditor getDefaultEditor(Class<?> columnClass) {
1390+ if (columnClass == null) {
1391+ return null;
1392+ }
1393+ else {
1394+ Object editor = defaultEditorsByColumnClass.get(columnClass);
1395+ if (editor != null) {
1396+ return (TableCellEditor)editor;
1397+ }
1398+ else {
1399+ return getDefaultEditor(columnClass.getSuperclass());
1400+ }
1401+ }
1402+ }
1403+
1404+ /**
1405+ * Turns on or off automatic drag handling. In order to enable automatic
1406+ * drag handling, this property should be set to {@code true}, and the
1407+ * table's {@code TransferHandler} needs to be {@code non-null}.
1408+ * The default value of the {@code dragEnabled} property is {@code false}.
1409+ * <p>
1410+ * The job of honoring this property, and recognizing a user drag gesture,
1411+ * lies with the look and feel implementation, and in particular, the table's
1412+ * {@code TableUI}. When automatic drag handling is enabled, most look and
1413+ * feels (including those that subclass {@code BasicLookAndFeel}) begin a
1414+ * drag and drop operation whenever the user presses the mouse button over
1415+ * an item (in single selection mode) or a selection (in other selection
1416+ * modes) and then moves the mouse a few pixels. Setting this property to
1417+ * {@code true} can therefore have a subtle effect on how selections behave.
1418+ * <p>
1419+ * If a look and feel is used that ignores this property, you can still
1420+ * begin a drag and drop operation by calling {@code exportAsDrag} on the
1421+ * table's {@code TransferHandler}.
1422+ *
1423+ * @param b whether or not to enable automatic drag handling
1424+ * @exception HeadlessException if
1425+ * <code>b</code> is <code>true</code> and
1426+ * <code>GraphicsEnvironment.isHeadless()</code>
1427+ * returns <code>true</code>
1428+ * @see java.awt.GraphicsEnvironment#isHeadless
1429+ * @see #getDragEnabled
1430+ * @see #setTransferHandler
1431+ * @see TransferHandler
1432+ * @since 1.4
1433+ *
1434+ * @beaninfo
1435+ * description: determines whether automatic drag handling is enabled
1436+ * bound: false
1437+ */
1438+ public void setDragEnabled(boolean b) {
1439+ if (b && GraphicsEnvironment.isHeadless()) {
1440+ throw new HeadlessException();
1441+ }
1442+ dragEnabled = b;
1443+ }
1444+
1445+ /**
1446+ * Returns whether or not automatic drag handling is enabled.
1447+ *
1448+ * @return the value of the {@code dragEnabled} property
1449+ * @see #setDragEnabled
1450+ * @since 1.4
1451+ */
1452+ public boolean getDragEnabled() {
1453+ return dragEnabled;
1454+ }
1455+
1456+ /**
1457+ * Sets the drop mode for this component. For backward compatibility,
1458+ * the default for this property is <code>DropMode.USE_SELECTION</code>.
1459+ * Usage of one of the other modes is recommended, however, for an
1460+ * improved user experience. <code>DropMode.ON</code>, for instance,
1461+ * offers similar behavior of showing items as selected, but does so without
1462+ * affecting the actual selection in the table.
1463+ * <p>
1464+ * <code>JTable</code> supports the following drop modes:
1465+ * <ul>
1466+ * <li><code>DropMode.USE_SELECTION</code></li>
1467+ * <li><code>DropMode.ON</code></li>
1468+ * <li><code>DropMode.INSERT</code></li>
1469+ * <li><code>DropMode.INSERT_ROWS</code></li>
1470+ * <li><code>DropMode.INSERT_COLS</code></li>
1471+ * <li><code>DropMode.ON_OR_INSERT</code></li>
1472+ * <li><code>DropMode.ON_OR_INSERT_ROWS</code></li>
1473+ * <li><code>DropMode.ON_OR_INSERT_COLS</code></li>
1474+ * </ul>
1475+ * <p>
1476+ * The drop mode is only meaningful if this component has a
1477+ * <code>TransferHandler</code> that accepts drops.
1478+ *
1479+ * @param dropMode the drop mode to use
1480+ * @throws IllegalArgumentException if the drop mode is unsupported
1481+ * or <code>null</code>
1482+ * @see #getDropMode
1483+ * @see #getDropLocation
1484+ * @see #setTransferHandler
1485+ * @see TransferHandler
1486+ * @since 1.6
1487+ */
1488+ public final void setDropMode(DropMode dropMode) {
1489+ if (dropMode != null) {
1490+ switch (dropMode) {
1491+ case USE_SELECTION:
1492+ case ON:
1493+ case INSERT:
1494+ case INSERT_ROWS:
1495+ case INSERT_COLS:
1496+ case ON_OR_INSERT:
1497+ case ON_OR_INSERT_ROWS:
1498+ case ON_OR_INSERT_COLS:
1499+ this.dropMode = dropMode;
1500+ return;
1501+ }
1502+ }
1503+
1504+ throw new IllegalArgumentException(dropMode + ": Unsupported drop mode for table");
1505+ }
1506+
1507+ /**
1508+ * Returns the drop mode for this component.
1509+ *
1510+ * @return the drop mode for this component
1511+ * @see #setDropMode
1512+ * @since 1.6
1513+ */
1514+ public final DropMode getDropMode() {
1515+ return dropMode;
1516+ }
1517+
1518+ /**
1519+ * Calculates a drop location in this component, representing where a
1520+ * drop at the given point should insert data.
1521+ *
1522+ * @param p the point to calculate a drop location for
1523+ * @return the drop location, or <code>null</code>
1524+ */
1525+ DropLocation dropLocationForPoint(Point p) {
1526+ DropLocation location = null;
1527+
1528+ int row = rowAtPoint(p);
1529+ int col = columnAtPoint(p);
1530+ boolean outside = Boolean.TRUE == getClientProperty("Table.isFileList")
1531+ && SwingUtilities2.pointOutsidePrefSize(this, row, col, p);
1532+
1533+ Rectangle rect = getCellRect(row, col, true);
1534+ Section xSection, ySection;
1535+ boolean between = false;
1536+ boolean ltr = getComponentOrientation().isLeftToRight();
1537+
1538+ switch(dropMode) {
1539+ case USE_SELECTION:
1540+ case ON:
1541+ if (row == -1 || col == -1 || outside) {
1542+ location = new DropLocation(p, -1, -1, false, false);
1543+ } else {
1544+ location = new DropLocation(p, row, col, false, false);
1545+ }
1546+ break;
1547+ case INSERT:
1548+ if (row == -1 && col == -1) {
1549+ location = new DropLocation(p, 0, 0, true, true);
1550+ break;
1551+ }
1552+
1553+ xSection = SwingUtilities2.liesInHorizontal(rect, p, ltr, true);
1554+
1555+ if (row == -1) {
1556+ if (xSection == LEADING) {
1557+ location = new DropLocation(p, getRowCount(), col, true, true);
1558+ } else if (xSection == TRAILING) {
1559+ location = new DropLocation(p, getRowCount(), col + 1, true, true);
1560+ } else {
1561+ location = new DropLocation(p, getRowCount(), col, true, false);
1562+ }
1563+ } else if (xSection == LEADING || xSection == TRAILING) {
1564+ ySection = SwingUtilities2.liesInVertical(rect, p, true);
1565+ if (ySection == LEADING) {
1566+ between = true;
1567+ } else if (ySection == TRAILING) {
1568+ row++;
1569+ between = true;
1570+ }
1571+
1572+ location = new DropLocation(p, row,
1573+ xSection == TRAILING ? col + 1 : col,
1574+ between, true);
1575+ } else {
1576+ if (SwingUtilities2.liesInVertical(rect, p, false) == TRAILING) {
1577+ row++;
1578+ }
1579+
1580+ location = new DropLocation(p, row, col, true, false);
1581+ }
1582+
1583+ break;
1584+ case INSERT_ROWS:
1585+ if (row == -1 && col == -1) {
1586+ location = new DropLocation(p, -1, -1, false, false);
1587+ break;
1588+ }
1589+
1590+ if (row == -1) {
1591+ location = new DropLocation(p, getRowCount(), col, true, false);
1592+ break;
1593+ }
1594+
1595+ if (SwingUtilities2.liesInVertical(rect, p, false) == TRAILING) {
1596+ row++;
1597+ }
1598+
1599+ location = new DropLocation(p, row, col, true, false);
1600+ break;
1601+ case ON_OR_INSERT_ROWS:
1602+ if (row == -1 && col == -1) {
1603+ location = new DropLocation(p, -1, -1, false, false);
1604+ break;
1605+ }
1606+
1607+ if (row == -1) {
1608+ location = new DropLocation(p, getRowCount(), col, true, false);
1609+ break;
1610+ }
1611+
1612+ ySection = SwingUtilities2.liesInVertical(rect, p, true);
1613+ if (ySection == LEADING) {
1614+ between = true;
1615+ } else if (ySection == TRAILING) {
1616+ row++;
1617+ between = true;
1618+ }
1619+
1620+ location = new DropLocation(p, row, col, between, false);
1621+ break;
1622+ case INSERT_COLS:
1623+ if (row == -1) {
1624+ location = new DropLocation(p, -1, -1, false, false);
1625+ break;
1626+ }
1627+
1628+ if (col == -1) {
1629+ location = new DropLocation(p, getColumnCount(), col, false, true);
1630+ break;
1631+ }
1632+
1633+ if (SwingUtilities2.liesInHorizontal(rect, p, ltr, false) == TRAILING) {
1634+ col++;
1635+ }
1636+
1637+ location = new DropLocation(p, row, col, false, true);
1638+ break;
1639+ case ON_OR_INSERT_COLS:
1640+ if (row == -1) {
1641+ location = new DropLocation(p, -1, -1, false, false);
1642+ break;
1643+ }
1644+
1645+ if (col == -1) {
1646+ location = new DropLocation(p, row, getColumnCount(), false, true);
1647+ break;
1648+ }
1649+
1650+ xSection = SwingUtilities2.liesInHorizontal(rect, p, ltr, true);
1651+ if (xSection == LEADING) {
1652+ between = true;
1653+ } else if (xSection == TRAILING) {
1654+ col++;
1655+ between = true;
1656+ }
1657+
1658+ location = new DropLocation(p, row, col, false, between);
1659+ break;
1660+ case ON_OR_INSERT:
1661+ if (row == -1 && col == -1) {
1662+ location = new DropLocation(p, 0, 0, true, true);
1663+ break;
1664+ }
1665+
1666+ xSection = SwingUtilities2.liesInHorizontal(rect, p, ltr, true);
1667+
1668+ if (row == -1) {
1669+ if (xSection == LEADING) {
1670+ location = new DropLocation(p, getRowCount(), col, true, true);
1671+ } else if (xSection == TRAILING) {
1672+ location = new DropLocation(p, getRowCount(), col + 1, true, true);
1673+ } else {
1674+ location = new DropLocation(p, getRowCount(), col, true, false);
1675+ }
1676+
1677+ break;
1678+ }
1679+
1680+ ySection = SwingUtilities2.liesInVertical(rect, p, true);
1681+ if (ySection == LEADING) {
1682+ between = true;
1683+ } else if (ySection == TRAILING) {
1684+ row++;
1685+ between = true;
1686+ }
1687+
1688+ location = new DropLocation(p, row,
1689+ xSection == TRAILING ? col + 1 : col,
1690+ between,
1691+ xSection != MIDDLE);
1692+
1693+ break;
1694+ default:
1695+ assert false : "Unexpected drop mode";
1696+ }
1697+
1698+ return location;
1699+ }
1700+
1701+ /**
1702+ * Called to set or clear the drop location during a DnD operation.
1703+ * In some cases, the component may need to use it's internal selection
1704+ * temporarily to indicate the drop location. To help facilitate this,
1705+ * this method returns and accepts as a parameter a state object.
1706+ * This state object can be used to store, and later restore, the selection
1707+ * state. Whatever this method returns will be passed back to it in
1708+ * future calls, as the state parameter. If it wants the DnD system to
1709+ * continue storing the same state, it must pass it back every time.
1710+ * Here's how this is used:
1711+ * <p>
1712+ * Let's say that on the first call to this method the component decides
1713+ * to save some state (because it is about to use the selection to show
1714+ * a drop index). It can return a state object to the caller encapsulating
1715+ * any saved selection state. On a second call, let's say the drop location
1716+ * is being changed to something else. The component doesn't need to
1717+ * restore anything yet, so it simply passes back the same state object
1718+ * to have the DnD system continue storing it. Finally, let's say this
1719+ * method is messaged with <code>null</code>. This means DnD
1720+ * is finished with this component for now, meaning it should restore
1721+ * state. At this point, it can use the state parameter to restore
1722+ * said state, and of course return <code>null</code> since there's
1723+ * no longer anything to store.
1724+ *
1725+ * @param location the drop location (as calculated by
1726+ * <code>dropLocationForPoint</code>) or <code>null</code>
1727+ * if there's no longer a valid drop location
1728+ * @param state the state object saved earlier for this component,
1729+ * or <code>null</code>
1730+ * @param forDrop whether or not the method is being called because an
1731+ * actual drop occurred
1732+ * @return any saved state for this component, or <code>null</code> if none
1733+ */
1734+ Object setDropLocation(TransferHandler.DropLocation location,
1735+ Object state,
1736+ boolean forDrop) {
1737+
1738+ Object retVal = null;
1739+ DropLocation tableLocation = (DropLocation)location;
1740+
1741+ if (dropMode == DropMode.USE_SELECTION) {
1742+ if (tableLocation == null) {
1743+ if (!forDrop && state != null) {
1744+ clearSelection();
1745+
1746+ int[] rows = ((int[][])state)[0];
1747+ int[] cols = ((int[][])state)[1];
1748+ int[] anchleads = ((int[][])state)[2];
1749+
1750+ for (int row : rows) {
1751+ addRowSelectionInterval(row, row);
1752+ }
1753+
1754+ for (int col : cols) {
1755+ addColumnSelectionInterval(col, col);
1756+ }
1757+
1758+ SwingUtilities2.setLeadAnchorWithoutSelection(
1759+ getSelectionModel(), anchleads[1], anchleads[0]);
1760+
1761+ SwingUtilities2.setLeadAnchorWithoutSelection(
1762+ getColumnModel().getSelectionModel(),
1763+ anchleads[3], anchleads[2]);
1764+ }
1765+ } else {
1766+ if (dropLocation == null) {
1767+ retVal = new int[][]{
1768+ getSelectedRows(),
1769+ getSelectedColumns(),
1770+ {getAdjustedIndex(getSelectionModel()
1771+ .getAnchorSelectionIndex(), true),
1772+ getAdjustedIndex(getSelectionModel()
1773+ .getLeadSelectionIndex(), true),
1774+ getAdjustedIndex(getColumnModel().getSelectionModel()
1775+ .getAnchorSelectionIndex(), false),
1776+ getAdjustedIndex(getColumnModel().getSelectionModel()
1777+ .getLeadSelectionIndex(), false)}};
1778+ } else {
1779+ retVal = state;
1780+ }
1781+
1782+ if (tableLocation.getRow() == -1) {
1783+ clearSelectionAndLeadAnchor();
1784+ } else {
1785+ setRowSelectionInterval(tableLocation.getRow(),
1786+ tableLocation.getRow());
1787+ setColumnSelectionInterval(tableLocation.getColumn(),
1788+ tableLocation.getColumn());
1789+ }
1790+ }
1791+ }
1792+
1793+ DropLocation old = dropLocation;
1794+ dropLocation = tableLocation;
1795+ firePropertyChange("dropLocation", old, dropLocation);
1796+
1797+ return retVal;
1798+ }
1799+
1800+ /**
1801+ * Returns the location that this component should visually indicate
1802+ * as the drop location during a DnD operation over the component,
1803+ * or {@code null} if no location is to currently be shown.
1804+ * <p>
1805+ * This method is not meant for querying the drop location
1806+ * from a {@code TransferHandler}, as the drop location is only
1807+ * set after the {@code TransferHandler}'s <code>canImport</code>
1808+ * has returned and has allowed for the location to be shown.
1809+ * <p>
1810+ * When this property changes, a property change event with
1811+ * name "dropLocation" is fired by the component.
1812+ *
1813+ * @return the drop location
1814+ * @see #setDropMode
1815+ * @see TransferHandler#canImport(TransferHandler.TransferSupport)
1816+ * @since 1.6
1817+ */
1818+ public final DropLocation getDropLocation() {
1819+ return dropLocation;
1820+ }
1821+
1822+ /**
1823+ * Specifies whether a {@code RowSorter} should be created for the
1824+ * table whenever its model changes.
1825+ * <p>
1826+ * When {@code setAutoCreateRowSorter(true)} is invoked, a {@code
1827+ * TableRowSorter} is immediately created and installed on the
1828+ * table. While the {@code autoCreateRowSorter} property remains
1829+ * {@code true}, every time the model is changed, a new {@code
1830+ * TableRowSorter} is created and set as the table's row sorter.
1831+ *
1832+ * @param autoCreateRowSorter whether or not a {@code RowSorter}
1833+ * should be automatically created
1834+ * @see javax.swing.table.TableRowSorter
1835+ * @beaninfo
1836+ * bound: true
1837+ * preferred: true
1838+ * description: Whether or not to turn on sorting by default.
1839+ * @since 1.6
1840+ */
1841+ public void setAutoCreateRowSorter(boolean autoCreateRowSorter) {
1842+ boolean oldValue = this.autoCreateRowSorter;
1843+ this.autoCreateRowSorter = autoCreateRowSorter;
1844+ if (autoCreateRowSorter) {
1845+ setRowSorter(new TableRowSorter<TableModel>(getModel()));
1846+ }
1847+ firePropertyChange("autoCreateRowSorter", oldValue,
1848+ autoCreateRowSorter);
1849+ }
1850+
1851+ /**
1852+ * Returns {@code true} if whenever the model changes, a new
1853+ * {@code RowSorter} should be created and installed
1854+ * as the table's sorter; otherwise, returns {@code false}.
1855+ *
1856+ * @return true if a {@code RowSorter} should be created when
1857+ * the model changes
1858+ * @since 1.6
1859+ */
1860+ public boolean getAutoCreateRowSorter() {
1861+ return autoCreateRowSorter;
1862+ }
1863+
1864+ /**
1865+ * Specifies whether the selection should be updated after sorting.
1866+ * If true, on sorting the selection is reset such that
1867+ * the same rows, in terms of the model, remain selected. The default
1868+ * is true.
1869+ *
1870+ * @param update whether or not to update the selection on sorting
1871+ * @beaninfo
1872+ * bound: true
1873+ * expert: true
1874+ * description: Whether or not to update the selection on sorting
1875+ * @since 1.6
1876+ */
1877+ public void setUpdateSelectionOnSort(boolean update) {
1878+ if (updateSelectionOnSort != update) {
1879+ updateSelectionOnSort = update;
1880+ firePropertyChange("updateSelectionOnSort", !update, update);
1881+ }
1882+ }
1883+
1884+ /**
1885+ * Returns true if the selection should be updated after sorting.
1886+ *
1887+ * @return whether to update the selection on a sort
1888+ * @since 1.6
1889+ */
1890+ public boolean getUpdateSelectionOnSort() {
1891+ return updateSelectionOnSort;
1892+ }
1893+
1894+ /**
1895+ * Sets the <code>RowSorter</code>. <code>RowSorter</code> is used
1896+ * to provide sorting and filtering to a <code>JTable</code>.
1897+ * <p>
1898+ * This method clears the selection and resets any variable row heights.
1899+ * <p>
1900+ * This method fires a <code>PropertyChangeEvent</code> when appropriate,
1901+ * with the property name <code>"rowSorter"</code>. For
1902+ * backward-compatibility, this method fires an additional event with the
1903+ * property name <code>"sorter"</code>.
1904+ * <p>
1905+ * If the underlying model of the <code>RowSorter</code> differs from
1906+ * that of this <code>JTable</code> undefined behavior will result.
1907+ *
1908+ * @param sorter the <code>RowSorter</code>; <code>null</code> turns
1909+ * sorting off
1910+ * @see javax.swing.table.TableRowSorter
1911+ * @beaninfo
1912+ * bound: true
1913+ * description: The table's RowSorter
1914+ * @since 1.6
1915+ */
1916+ public void setRowSorter(RowSorter<? extends TableModel> sorter) {
1917+ RowSorter<? extends TableModel> oldRowSorter = null;
1918+ if (sortManager != null) {
1919+ oldRowSorter = sortManager.sorter;
1920+ sortManager.dispose();
1921+ sortManager = null;
1922+ }
1923+ rowModel = null;
1924+ clearSelectionAndLeadAnchor();
1925+ if (sorter != null) {
1926+ sortManager = new SortManager(sorter);
1927+ }
1928+ resizeAndRepaint();
1929+ firePropertyChange("rowSorter", oldRowSorter, sorter);
1930+ firePropertyChange("sorter", oldRowSorter, sorter);
1931+ }
1932+
1933+ /**
1934+ * Returns the object responsible for sorting.
1935+ *
1936+ * @return the object responsible for sorting
1937+ * @since 1.6
1938+ */
1939+ public RowSorter<? extends TableModel> getRowSorter() {
1940+ return (sortManager != null) ? sortManager.sorter : null;
1941+ }
1942+
1943+//
1944+// Selection methods
1945+//
1946+ /**
1947+ * Sets the table's selection mode to allow only single selections, a single
1948+ * contiguous interval, or multiple intervals.
1949+ * <P>
1950+ * <bold>Note:</bold>
1951+ * <code>JTable</code> provides all the methods for handling
1952+ * column and row selection. When setting states,
1953+ * such as <code>setSelectionMode</code>, it not only
1954+ * updates the mode for the row selection model but also sets similar
1955+ * values in the selection model of the <code>columnModel</code>.
1956+ * If you want to have the row and column selection models operating
1957+ * in different modes, set them both directly.
1958+ * <p>
1959+ * Both the row and column selection models for <code>JTable</code>
1960+ * default to using a <code>DefaultListSelectionModel</code>
1961+ * so that <code>JTable</code> works the same way as the
1962+ * <code>JList</code>. See the <code>setSelectionMode</code> method
1963+ * in <code>JList</code> for details about the modes.
1964+ *
1965+ * @see JList#setSelectionMode
1966+ * @beaninfo
1967+ * description: The selection mode used by the row and column selection models.
1968+ * enum: SINGLE_SELECTION ListSelectionModel.SINGLE_SELECTION
1969+ * SINGLE_INTERVAL_SELECTION ListSelectionModel.SINGLE_INTERVAL_SELECTION
1970+ * MULTIPLE_INTERVAL_SELECTION ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
1971+ */
1972+ public void setSelectionMode(int selectionMode) {
1973+ clearSelection();
1974+ getSelectionModel().setSelectionMode(selectionMode);
1975+ getColumnModel().getSelectionModel().setSelectionMode(selectionMode);
1976+ }
1977+
1978+ /**
1979+ * Sets whether the rows in this model can be selected.
1980+ *
1981+ * @param rowSelectionAllowed true if this model will allow row selection
1982+ * @see #getRowSelectionAllowed
1983+ * @beaninfo
1984+ * bound: true
1985+ * attribute: visualUpdate true
1986+ * description: If true, an entire row is selected for each selected cell.
1987+ */
1988+ public void setRowSelectionAllowed(boolean rowSelectionAllowed) {
1989+ boolean old = this.rowSelectionAllowed;
1990+ this.rowSelectionAllowed = rowSelectionAllowed;
1991+ if (old != rowSelectionAllowed) {
1992+ repaint();
1993+ }
1994+ firePropertyChange("rowSelectionAllowed", old, rowSelectionAllowed);
1995+ }
1996+
1997+ /**
1998+ * Returns true if rows can be selected.
1999+ *
2000+ * @return true if rows can be selected, otherwise false
2001+ * @see #setRowSelectionAllowed
2002+ */
2003+ public boolean getRowSelectionAllowed() {
2004+ return rowSelectionAllowed;
2005+ }
2006+
2007+ /**
2008+ * Sets whether the columns in this model can be selected.
2009+ *
2010+ * @param columnSelectionAllowed true if this model will allow column selection
2011+ * @see #getColumnSelectionAllowed
2012+ * @beaninfo
2013+ * bound: true
2014+ * attribute: visualUpdate true
2015+ * description: If true, an entire column is selected for each selected cell.
2016+ */
2017+ public void setColumnSelectionAllowed(boolean columnSelectionAllowed) {
2018+ boolean old = columnModel.getColumnSelectionAllowed();
2019+ columnModel.setColumnSelectionAllowed(columnSelectionAllowed);
2020+ if (old != columnSelectionAllowed) {
2021+ repaint();
2022+ }
2023+ firePropertyChange("columnSelectionAllowed", old, columnSelectionAllowed);
2024+ }
2025+
2026+ /**
2027+ * Returns true if columns can be selected.
2028+ *
2029+ * @return true if columns can be selected, otherwise false
2030+ * @see #setColumnSelectionAllowed
2031+ */
2032+ public boolean getColumnSelectionAllowed() {
2033+ return columnModel.getColumnSelectionAllowed();
2034+ }
2035+
2036+ /**
2037+ * Sets whether this table allows both a column selection and a
2038+ * row selection to exist simultaneously. When set,
2039+ * the table treats the intersection of the row and column selection
2040+ * models as the selected cells. Override <code>isCellSelected</code> to
2041+ * change this default behavior. This method is equivalent to setting
2042+ * both the <code>rowSelectionAllowed</code> property and
2043+ * <code>columnSelectionAllowed</code> property of the
2044+ * <code>columnModel</code> to the supplied value.
2045+ *
2046+ * @param cellSelectionEnabled true if simultaneous row and column
2047+ * selection is allowed
2048+ * @see #getCellSelectionEnabled
2049+ * @see #isCellSelected
2050+ * @beaninfo
2051+ * bound: true
2052+ * attribute: visualUpdate true
2053+ * description: Select a rectangular region of cells rather than
2054+ * rows or columns.
2055+ */
2056+ public void setCellSelectionEnabled(boolean cellSelectionEnabled) {
2057+ setRowSelectionAllowed(cellSelectionEnabled);
2058+ setColumnSelectionAllowed(cellSelectionEnabled);
2059+ boolean old = this.cellSelectionEnabled;
2060+ this.cellSelectionEnabled = cellSelectionEnabled;
2061+ firePropertyChange("cellSelectionEnabled", old, cellSelectionEnabled);
2062+ }
2063+
2064+ /**
2065+ * Returns true if both row and column selection models are enabled.
2066+ * Equivalent to <code>getRowSelectionAllowed() &&
2067+ * getColumnSelectionAllowed()</code>.
2068+ *
2069+ * @return true if both row and column selection models are enabled
2070+ *
2071+ * @see #setCellSelectionEnabled
2072+ */
2073+ public boolean getCellSelectionEnabled() {
2074+ return getRowSelectionAllowed() && getColumnSelectionAllowed();
2075+ }
2076+
2077+ /**
2078+ * Selects all rows, columns, and cells in the table.
2079+ */
2080+ public void selectAll() {
2081+ // If I'm currently editing, then I should stop editing
2082+ if (isEditing()) {
2083+ removeEditor();
2084+ }
2085+ if (getRowCount() > 0 && getColumnCount() > 0) {
2086+ int oldLead;
2087+ int oldAnchor;
2088+ ListSelectionModel selModel;
2089+
2090+ selModel = selectionModel;
2091+ selModel.setValueIsAdjusting(true);
2092+ oldLead = getAdjustedIndex(selModel.getLeadSelectionIndex(), true);
2093+ oldAnchor = getAdjustedIndex(selModel.getAnchorSelectionIndex(), true);
2094+
2095+ setRowSelectionInterval(0, getRowCount()-1);
2096+
2097+ // this is done to restore the anchor and lead
2098+ SwingUtilities2.setLeadAnchorWithoutSelection(selModel, oldLead, oldAnchor);
2099+
2100+ selModel.setValueIsAdjusting(false);
2101+
2102+ selModel = columnModel.getSelectionModel();
2103+ selModel.setValueIsAdjusting(true);
2104+ oldLead = getAdjustedIndex(selModel.getLeadSelectionIndex(), false);
2105+ oldAnchor = getAdjustedIndex(selModel.getAnchorSelectionIndex(), false);
2106+
2107+ setColumnSelectionInterval(0, getColumnCount()-1);
2108+
2109+ // this is done to restore the anchor and lead
2110+ SwingUtilities2.setLeadAnchorWithoutSelection(selModel, oldLead, oldAnchor);
2111+
2112+ selModel.setValueIsAdjusting(false);
2113+ }
2114+ }
2115+
2116+ /**
2117+ * Deselects all selected columns and rows.
2118+ */
2119+ public void clearSelection() {
2120+ selectionModel.clearSelection();
2121+ columnModel.getSelectionModel().clearSelection();
2122+ }
2123+
2124+ private void clearSelectionAndLeadAnchor() {
2125+ selectionModel.setValueIsAdjusting(true);
2126+ columnModel.getSelectionModel().setValueIsAdjusting(true);
2127+
2128+ clearSelection();
2129+
2130+ selectionModel.setAnchorSelectionIndex(-1);
2131+ selectionModel.setLeadSelectionIndex(-1);
2132+ columnModel.getSelectionModel().setAnchorSelectionIndex(-1);
2133+ columnModel.getSelectionModel().setLeadSelectionIndex(-1);
2134+
2135+ selectionModel.setValueIsAdjusting(false);
2136+ columnModel.getSelectionModel().setValueIsAdjusting(false);
2137+ }
2138+
2139+ private int getAdjustedIndex(int index, boolean row) {
2140+ int compare = row ? getRowCount() : getColumnCount();
2141+ return index < compare ? index : -1;
2142+ }
2143+
2144+ private int boundRow(int row) throws IllegalArgumentException {
2145+ if (row < 0 || row >= getRowCount()) {
2146+ throw new IllegalArgumentException("Row index out of range");
2147+ }
2148+ return row;
2149+ }
2150+
2151+ private int boundColumn(int col) {
2152+ if (col< 0 || col >= getColumnCount()) {
2153+ throw new IllegalArgumentException("Column index out of range");
2154+ }
2155+ return col;
2156+ }
2157+
2158+ /**
2159+ * Selects the rows from <code>index0</code> to <code>index1</code>,
2160+ * inclusive.
2161+ *
2162+ * @exception IllegalArgumentException if <code>index0</code> or
2163+ * <code>index1</code> lie outside
2164+ * [0, <code>getRowCount()</code>-1]
2165+ * @param index0 one end of the interval
2166+ * @param index1 the other end of the interval
2167+ */
2168+ public void setRowSelectionInterval(int index0, int index1) {
2169+ selectionModel.setSelectionInterval(boundRow(index0), boundRow(index1));
2170+ }
2171+
2172+ /**
2173+ * Selects the columns from <code>index0</code> to <code>index1</code>,
2174+ * inclusive.
2175+ *
2176+ * @exception IllegalArgumentException if <code>index0</code> or
2177+ * <code>index1</code> lie outside
2178+ * [0, <code>getColumnCount()</code>-1]
2179+ * @param index0 one end of the interval
2180+ * @param index1 the other end of the interval
2181+ */
2182+ public void setColumnSelectionInterval(int index0, int index1) {
2183+ columnModel.getSelectionModel().setSelectionInterval(boundColumn(index0), boundColumn(index1));
2184+ }
2185+
2186+ /**
2187+ * Adds the rows from <code>index0</code> to <code>index1</code>, inclusive, to
2188+ * the current selection.
2189+ *
2190+ * @exception IllegalArgumentException if <code>index0</code> or <code>index1</code>
2191+ * lie outside [0, <code>getRowCount()</code>-1]
2192+ * @param index0 one end of the interval
2193+ * @param index1 the other end of the interval
2194+ */
2195+ public void addRowSelectionInterval(int index0, int index1) {
2196+ selectionModel.addSelectionInterval(boundRow(index0), boundRow(index1));
2197+ }
2198+
2199+ /**
2200+ * Adds the columns from <code>index0</code> to <code>index1</code>,
2201+ * inclusive, to the current selection.
2202+ *
2203+ * @exception IllegalArgumentException if <code>index0</code> or
2204+ * <code>index1</code> lie outside
2205+ * [0, <code>getColumnCount()</code>-1]
2206+ * @param index0 one end of the interval
2207+ * @param index1 the other end of the interval
2208+ */
2209+ public void addColumnSelectionInterval(int index0, int index1) {
2210+ columnModel.getSelectionModel().addSelectionInterval(boundColumn(index0), boundColumn(index1));
2211+ }
2212+
2213+ /**
2214+ * Deselects the rows from <code>index0</code> to <code>index1</code>, inclusive.
2215+ *
2216+ * @exception IllegalArgumentException if <code>index0</code> or
2217+ * <code>index1</code> lie outside
2218+ * [0, <code>getRowCount()</code>-1]
2219+ * @param index0 one end of the interval
2220+ * @param index1 the other end of the interval
2221+ */
2222+ public void removeRowSelectionInterval(int index0, int index1) {
2223+ selectionModel.removeSelectionInterval(boundRow(index0), boundRow(index1));
2224+ }
2225+
2226+ /**
2227+ * Deselects the columns from <code>index0</code> to <code>index1</code>, inclusive.
2228+ *
2229+ * @exception IllegalArgumentException if <code>index0</code> or
2230+ * <code>index1</code> lie outside
2231+ * [0, <code>getColumnCount()</code>-1]
2232+ * @param index0 one end of the interval
2233+ * @param index1 the other end of the interval
2234+ */
2235+ public void removeColumnSelectionInterval(int index0, int index1) {
2236+ columnModel.getSelectionModel().removeSelectionInterval(boundColumn(index0), boundColumn(index1));
2237+ }
2238+
2239+ /**
2240+ * Returns the index of the first selected row, -1 if no row is selected.
2241+ * @return the index of the first selected row
2242+ */
2243+ public int getSelectedRow() {
2244+ return selectionModel.getMinSelectionIndex();
2245+ }
2246+
2247+ /**
2248+ * Returns the index of the first selected column,
2249+ * -1 if no column is selected.
2250+ * @return the index of the first selected column
2251+ */
2252+ public int getSelectedColumn() {
2253+ return columnModel.getSelectionModel().getMinSelectionIndex();
2254+ }
2255+
2256+ /**
2257+ * Returns the indices of all selected rows.
2258+ *
2259+ * @return an array of integers containing the indices of all selected rows,
2260+ * or an empty array if no row is selected
2261+ * @see #getSelectedRow
2262+ */
2263+ public int[] getSelectedRows() {
2264+ int iMin = selectionModel.getMinSelectionIndex();
2265+ int iMax = selectionModel.getMaxSelectionIndex();
2266+
2267+ if ((iMin == -1) || (iMax == -1)) {
2268+ return new int[0];
2269+ }
2270+
2271+ int[] rvTmp = new int[1+ (iMax - iMin)];
2272+ int n = 0;
2273+ for(int i = iMin; i <= iMax; i++) {
2274+ if (selectionModel.isSelectedIndex(i)) {
2275+ rvTmp[n++] = i;
2276+ }
2277+ }
2278+ int[] rv = new int[n];
2279+ System.arraycopy(rvTmp, 0, rv, 0, n);
2280+ return rv;
2281+ }
2282+
2283+ /**
2284+ * Returns the indices of all selected columns.
2285+ *
2286+ * @return an array of integers containing the indices of all selected columns,
2287+ * or an empty array if no column is selected
2288+ * @see #getSelectedColumn
2289+ */
2290+ public int[] getSelectedColumns() {
2291+ return columnModel.getSelectedColumns();
2292+ }
2293+
2294+ /**
2295+ * Returns the number of selected rows.
2296+ *
2297+ * @return the number of selected rows, 0 if no rows are selected
2298+ */
2299+ public int getSelectedRowCount() {
2300+ int iMin = selectionModel.getMinSelectionIndex();
2301+ int iMax = selectionModel.getMaxSelectionIndex();
2302+ int count = 0;
2303+
2304+ for(int i = iMin; i <= iMax; i++) {
2305+ if (selectionModel.isSelectedIndex(i)) {
2306+ count++;
2307+ }
2308+ }
2309+ return count;
2310+ }
2311+
2312+ /**
2313+ * Returns the number of selected columns.
2314+ *
2315+ * @return the number of selected columns, 0 if no columns are selected
2316+ */
2317+ public int getSelectedColumnCount() {
2318+ return columnModel.getSelectedColumnCount();
2319+ }
2320+
2321+ /**
2322+ * Returns true if the specified index is in the valid range of rows,
2323+ * and the row at that index is selected.
2324+ *
2325+ * @return true if <code>row</code> is a valid index and the row at
2326+ * that index is selected (where 0 is the first row)
2327+ */
2328+ public boolean isRowSelected(int row) {
2329+ return selectionModel.isSelectedIndex(row);
2330+ }
2331+
2332+ /**
2333+ * Returns true if the specified index is in the valid range of columns,
2334+ * and the column at that index is selected.
2335+ *
2336+ * @param column the column in the column model
2337+ * @return true if <code>column</code> is a valid index and the column at
2338+ * that index is selected (where 0 is the first column)
2339+ */
2340+ public boolean isColumnSelected(int column) {
2341+ return columnModel.getSelectionModel().isSelectedIndex(column);
2342+ }
2343+
2344+ /**
2345+ * Returns true if the specified indices are in the valid range of rows
2346+ * and columns and the cell at the specified position is selected.
2347+ * @param row the row being queried
2348+ * @param column the column being queried
2349+ *
2350+ * @return true if <code>row</code> and <code>column</code> are valid indices
2351+ * and the cell at index <code>(row, column)</code> is selected,
2352+ * where the first row and first column are at index 0
2353+ */
2354+ public boolean isCellSelected(int row, int column) {
2355+ if (!getRowSelectionAllowed() && !getColumnSelectionAllowed()) {
2356+ return false;
2357+ }
2358+ return (!getRowSelectionAllowed() || isRowSelected(row)) &&
2359+ (!getColumnSelectionAllowed() || isColumnSelected(column));
2360+ }
2361+
2362+ private void changeSelectionModel(ListSelectionModel sm, int index,
2363+ boolean toggle, boolean extend, boolean selected,
2364+ int anchor, boolean anchorSelected) {
2365+ if (extend) {
2366+ if (toggle) {
2367+ if (anchorSelected) {
2368+ sm.addSelectionInterval(anchor, index);
2369+ } else {
2370+ sm.removeSelectionInterval(anchor, index);
2371+ // this is a Windows-only behavior that we want for file lists
2372+ if (Boolean.TRUE == getClientProperty("Table.isFileList")) {
2373+ sm.addSelectionInterval(index, index);
2374+ sm.setAnchorSelectionIndex(anchor);
2375+ }
2376+ }
2377+ }
2378+ else {
2379+ sm.setSelectionInterval(anchor, index);
2380+ }
2381+ }
2382+ else {
2383+ if (toggle) {
2384+ if (selected) {
2385+ sm.removeSelectionInterval(index, index);
2386+ }
2387+ else {
2388+ sm.addSelectionInterval(index, index);
2389+ }
2390+ }
2391+ else {
2392+ sm.setSelectionInterval(index, index);
2393+ }
2394+ }
2395+ }
2396+
2397+ /**
2398+ * Updates the selection models of the table, depending on the state of the
2399+ * two flags: <code>toggle</code> and <code>extend</code>. Most changes
2400+ * to the selection that are the result of keyboard or mouse events received
2401+ * by the UI are channeled through this method so that the behavior may be
2402+ * overridden by a subclass. Some UIs may need more functionality than
2403+ * this method provides, such as when manipulating the lead for discontiguous
2404+ * selection, and may not call into this method for some selection changes.
2405+ * <p>
2406+ * This implementation uses the following conventions:
2407+ * <ul>
2408+ * <li> <code>toggle</code>: <em>false</em>, <code>extend</code>: <em>false</em>.
2409+ * Clear the previous selection and ensure the new cell is selected.
2410+ * <li> <code>toggle</code>: <em>false</em>, <code>extend</code>: <em>true</em>.
2411+ * Extend the previous selection from the anchor to the specified cell,
2412+ * clearing all other selections.
2413+ * <li> <code>toggle</code>: <em>true</em>, <code>extend</code>: <em>false</em>.
2414+ * If the specified cell is selected, deselect it. If it is not selected, select it.
2415+ * <li> <code>toggle</code>: <em>true</em>, <code>extend</code>: <em>true</em>.
2416+ * Apply the selection state of the anchor to all cells between it and the
2417+ * specified cell.
2418+ * </ul>
2419+ * @param rowIndex affects the selection at <code>row</code>
2420+ * @param columnIndex affects the selection at <code>column</code>
2421+ * @param toggle see description above
2422+ * @param extend if true, extend the current selection
2423+ *
2424+ * @since 1.3
2425+ */
2426+ public void changeSelection(int rowIndex, int columnIndex, boolean toggle, boolean extend) {
2427+ ListSelectionModel rsm = getSelectionModel();
2428+ ListSelectionModel csm = getColumnModel().getSelectionModel();
2429+
2430+ int anchorRow = getAdjustedIndex(rsm.getAnchorSelectionIndex(), true);
2431+ int anchorCol = getAdjustedIndex(csm.getAnchorSelectionIndex(), false);
2432+
2433+ boolean anchorSelected = true;
2434+
2435+ if (anchorRow == -1) {
2436+ if (getRowCount() > 0) {
2437+ anchorRow = 0;
2438+ }
2439+ anchorSelected = false;
2440+ }
2441+
2442+ if (anchorCol == -1) {
2443+ if (getColumnCount() > 0) {
2444+ anchorCol = 0;
2445+ }
2446+ anchorSelected = false;
2447+ }
2448+
2449+ // Check the selection here rather than in each selection model.
2450+ // This is significant in cell selection mode if we are supposed
2451+ // to be toggling the selection. In this case it is better to
2452+ // ensure that the cell's selection state will indeed be changed.
2453+ // If this were done in the code for the selection model it
2454+ // might leave a cell in selection state if the row was
2455+ // selected but the column was not - as it would toggle them both.
2456+ boolean selected = isCellSelected(rowIndex, columnIndex);
2457+ anchorSelected = anchorSelected && isCellSelected(anchorRow, anchorCol);
2458+
2459+ changeSelectionModel(csm, columnIndex, toggle, extend, selected,
2460+ anchorCol, anchorSelected);
2461+ changeSelectionModel(rsm, rowIndex, toggle, extend, selected,
2462+ anchorRow, anchorSelected);
2463+
2464+ // Scroll after changing the selection as blit scrolling is immediate,
2465+ // so that if we cause the repaint after the scroll we end up painting
2466+ // everything!
2467+ if (getAutoscrolls()) {
2468+ Rectangle cellRect = getCellRect(rowIndex, columnIndex, false);
2469+ if (cellRect != null) {
2470+ scrollRectToVisible(cellRect);
2471+ }
2472+ }
2473+ }
2474+
2475+ /**
2476+ * Returns the foreground color for selected cells.
2477+ *
2478+ * @return the <code>Color</code> object for the foreground property
2479+ * @see #setSelectionForeground
2480+ * @see #setSelectionBackground
2481+ */
2482+ public Color getSelectionForeground() {
2483+ return selectionForeground;
2484+ }
2485+
2486+ /**
2487+ * Sets the foreground color for selected cells. Cell renderers
2488+ * can use this color to render text and graphics for selected
2489+ * cells.
2490+ * <p>
2491+ * The default value of this property is defined by the look
2492+ * and feel implementation.
2493+ * <p>
2494+ * This is a <a href="http://java.sun.com/docs/books/tutorial/javabeans/properties/bound.html">JavaBeans</a> bound property.
2495+ *
2496+ * @param selectionForeground the <code>Color</code> to use in the foreground
2497+ * for selected list items
2498+ * @see #getSelectionForeground
2499+ * @see #setSelectionBackground
2500+ * @see #setForeground
2501+ * @see #setBackground
2502+ * @see #setFont
2503+ * @beaninfo
2504+ * bound: true
2505+ * description: A default foreground color for selected cells.
2506+ */
2507+ public void setSelectionForeground(Color selectionForeground) {
2508+ Color old = this.selectionForeground;
2509+ this.selectionForeground = selectionForeground;
2510+ firePropertyChange("selectionForeground", old, selectionForeground);
2511+ repaint();
2512+ }
2513+
2514+ /**
2515+ * Returns the background color for selected cells.
2516+ *
2517+ * @return the <code>Color</code> used for the background of selected list items
2518+ * @see #setSelectionBackground
2519+ * @see #setSelectionForeground
2520+ */
2521+ public Color getSelectionBackground() {
2522+ return selectionBackground;
2523+ }
2524+
2525+ /**
2526+ * Sets the background color for selected cells. Cell renderers
2527+ * can use this color to the fill selected cells.
2528+ * <p>
2529+ * The default value of this property is defined by the look
2530+ * and feel implementation.
2531+ * <p>
2532+ * This is a <a href="http://java.sun.com/docs/books/tutorial/javabeans/properties/bound.html">JavaBeans</a> bound property.
2533+ *
2534+ * @param selectionBackground the <code>Color</code> to use for the background
2535+ * of selected cells
2536+ * @see #getSelectionBackground
2537+ * @see #setSelectionForeground
2538+ * @see #setForeground
2539+ * @see #setBackground
2540+ * @see #setFont
2541+ * @beaninfo
2542+ * bound: true
2543+ * description: A default background color for selected cells.
2544+ */
2545+ public void setSelectionBackground(Color selectionBackground) {
2546+ Color old = this.selectionBackground;
2547+ this.selectionBackground = selectionBackground;
2548+ firePropertyChange("selectionBackground", old, selectionBackground);
2549+ repaint();
2550+ }
2551+
2552+ /**
2553+ * Returns the <code>TableColumn</code> object for the column in the table
2554+ * whose identifier is equal to <code>identifier</code>, when compared using
2555+ * <code>equals</code>.
2556+ *
2557+ * @return the <code>TableColumn</code> object that matches the identifier
2558+ * @exception IllegalArgumentException if <code>identifier</code> is <code>null</code> or no <code>TableColumn</code> has this identifier
2559+ *
2560+ * @param identifier the identifier object
2561+ */
2562+ public TableColumn getColumn(Object identifier) {
2563+ TableColumnModel cm = getColumnModel();
2564+ int columnIndex = cm.getColumnIndex(identifier);
2565+ return cm.getColumn(columnIndex);
2566+ }
2567+
2568+//
2569+// Informally implement the TableModel interface.
2570+//
2571+
2572+ /**
2573+ * Maps the index of the column in the view at
2574+ * <code>viewColumnIndex</code> to the index of the column
2575+ * in the table model. Returns the index of the corresponding
2576+ * column in the model. If <code>viewColumnIndex</code>
2577+ * is less than zero, returns <code>viewColumnIndex</code>.
2578+ *
2579+ * @param viewColumnIndex the index of the column in the view
2580+ * @return the index of the corresponding column in the model
2581+ *
2582+ * @see #convertColumnIndexToView
2583+ */
2584+ public int convertColumnIndexToModel(int viewColumnIndex) {
2585+ return SwingUtilities2.convertColumnIndexToModel(
2586+ getColumnModel(), viewColumnIndex);
2587+ }
2588+
2589+ /**
2590+ * Maps the index of the column in the table model at
2591+ * <code>modelColumnIndex</code> to the index of the column
2592+ * in the view. Returns the index of the
2593+ * corresponding column in the view; returns -1 if this column is not
2594+ * being displayed. If <code>modelColumnIndex</code> is less than zero,
2595+ * returns <code>modelColumnIndex</code>.
2596+ *
2597+ * @param modelColumnIndex the index of the column in the model
2598+ * @return the index of the corresponding column in the view
2599+ *
2600+ * @see #convertColumnIndexToModel
2601+ */
2602+ public int convertColumnIndexToView(int modelColumnIndex) {
2603+ return SwingUtilities2.convertColumnIndexToView(
2604+ getColumnModel(), modelColumnIndex);
2605+ }
2606+
2607+ /**
2608+ * Maps the index of the row in terms of the
2609+ * <code>TableModel</code> to the view. If the contents of the
2610+ * model are not sorted the model and view indices are the same.
2611+ *
2612+ * @param modelRowIndex the index of the row in terms of the model
2613+ * @return the index of the corresponding row in the view, or -1 if
2614+ * the row isn't visible
2615+ * @throws IndexOutOfBoundsException if sorting is enabled and passed an
2616+ * index outside the number of rows of the <code>TableModel</code>
2617+ * @see javax.swing.table.TableRowSorter
2618+ * @since 1.6
2619+ */
2620+ public int convertRowIndexToView(int modelRowIndex) {
2621+ RowSorter sorter = getRowSorter();
2622+ if (sorter != null) {
2623+ return sorter.convertRowIndexToView(modelRowIndex);
2624+ }
2625+ return modelRowIndex;
2626+ }
2627+
2628+ /**
2629+ * Maps the index of the row in terms of the view to the
2630+ * underlying <code>TableModel</code>. If the contents of the
2631+ * model are not sorted the model and view indices are the same.
2632+ *
2633+ * @param viewRowIndex the index of the row in the view
2634+ * @return the index of the corresponding row in the model
2635+ * @throws IndexOutOfBoundsException if sorting is enabled and passed an
2636+ * index outside the range of the <code>JTable</code> as
2637+ * determined by the method <code>getRowCount</code>
2638+ * @see javax.swing.table.TableRowSorter
2639+ * @see #getRowCount
2640+ * @since 1.6
2641+ */
2642+ public int convertRowIndexToModel(int viewRowIndex) {
2643+ RowSorter sorter = getRowSorter();
2644+ if (sorter != null) {
2645+ return sorter.convertRowIndexToModel(viewRowIndex);
2646+ }
2647+ return viewRowIndex;
2648+ }
2649+
2650+ /**
2651+ * Returns the number of rows that can be shown in the
2652+ * <code>JTable</code>, given unlimited space. If a
2653+ * <code>RowSorter</code> with a filter has been specified, the
2654+ * number of rows returned may differ from that of the underlying
2655+ * <code>TableModel</code>.
2656+ *
2657+ * @return the number of rows shown in the <code>JTable</code>
2658+ * @see #getColumnCount
2659+ */
2660+ public int getRowCount() {
2661+ RowSorter sorter = getRowSorter();
2662+ if (sorter != null) {
2663+ return sorter.getViewRowCount();
2664+ }
2665+ return getModel().getRowCount();
2666+ }
2667+
2668+ /**
2669+ * Returns the number of columns in the column model. Note that this may
2670+ * be different from the number of columns in the table model.
2671+ *
2672+ * @return the number of columns in the table
2673+ * @see #getRowCount
2674+ * @see #removeColumn
2675+ */
2676+ public int getColumnCount() {
2677+ return getColumnModel().getColumnCount();
2678+ }
2679+
2680+ /**
2681+ * Returns the name of the column appearing in the view at
2682+ * column position <code>column</code>.
2683+ *
2684+ * @param column the column in the view being queried
2685+ * @return the name of the column at position <code>column</code>
2686+ in the view where the first column is column 0
2687+ */
2688+ public String getColumnName(int column) {
2689+ return getModel().getColumnName(convertColumnIndexToModel(column));
2690+ }
2691+
2692+ /**
2693+ * Returns the type of the column appearing in the view at
2694+ * column position <code>column</code>.
2695+ *
2696+ * @param column the column in the view being queried
2697+ * @return the type of the column at position <code>column</code>
2698+ * in the view where the first column is column 0
2699+ */
2700+ public Class<?> getColumnClass(int column) {
2701+ return getModel().getColumnClass(convertColumnIndexToModel(column));
2702+ }
2703+
2704+ /**
2705+ * Returns the cell value at <code>row</code> and <code>column</code>.
2706+ * <p>
2707+ * <b>Note</b>: The column is specified in the table view's display
2708+ * order, and not in the <code>TableModel</code>'s column
2709+ * order. This is an important distinction because as the
2710+ * user rearranges the columns in the table,
2711+ * the column at a given index in the view will change.
2712+ * Meanwhile the user's actions never affect the model's
2713+ * column ordering.
2714+ *
2715+ * @param row the row whose value is to be queried
2716+ * @param column the column whose value is to be queried
2717+ * @return the Object at the specified cell
2718+ */
2719+ public Object getValueAt(int row, int column) {
2720+ return getModel().getValueAt(convertRowIndexToModel(row),
2721+ convertColumnIndexToModel(column));
2722+ }
2723+
2724+ /**
2725+ * Sets the value for the cell in the table model at <code>row</code>
2726+ * and <code>column</code>.
2727+ * <p>
2728+ * <b>Note</b>: The column is specified in the table view's display
2729+ * order, and not in the <code>TableModel</code>'s column
2730+ * order. This is an important distinction because as the
2731+ * user rearranges the columns in the table,
2732+ * the column at a given index in the view will change.
2733+ * Meanwhile the user's actions never affect the model's
2734+ * column ordering.
2735+ *
2736+ * <code>aValue</code> is the new value.
2737+ *
2738+ * @param aValue the new value
2739+ * @param row the row of the cell to be changed
2740+ * @param column the column of the cell to be changed
2741+ * @see #getValueAt
2742+ */
2743+ public void setValueAt(Object aValue, int row, int column) {
2744+ getModel().setValueAt(aValue, convertRowIndexToModel(row),
2745+ convertColumnIndexToModel(column));
2746+ }
2747+
2748+ /**
2749+ * Returns true if the cell at <code>row</code> and <code>column</code>
2750+ * is editable. Otherwise, invoking <code>setValueAt</code> on the cell
2751+ * will have no effect.
2752+ * <p>
2753+ * <b>Note</b>: The column is specified in the table view's display
2754+ * order, and not in the <code>TableModel</code>'s column
2755+ * order. This is an important distinction because as the
2756+ * user rearranges the columns in the table,
2757+ * the column at a given index in the view will change.
2758+ * Meanwhile the user's actions never affect the model's
2759+ * column ordering.
2760+ *
2761+ *
2762+ * @param row the row whose value is to be queried
2763+ * @param column the column whose value is to be queried
2764+ * @return true if the cell is editable
2765+ * @see #setValueAt
2766+ */
2767+ public boolean isCellEditable(int row, int column) {
2768+ return getModel().isCellEditable(convertRowIndexToModel(row),
2769+ convertColumnIndexToModel(column));
2770+ }
2771+//
2772+// Adding and removing columns in the view
2773+//
2774+
2775+ /**
2776+ * Appends <code>aColumn</code> to the end of the array of columns held by
2777+ * this <code>JTable</code>'s column model.
2778+ * If the column name of <code>aColumn</code> is <code>null</code>,
2779+ * sets the column name of <code>aColumn</code> to the name
2780+ * returned by <code>getModel().getColumnName()</code>.
2781+ * <p>
2782+ * To add a column to this <code>JTable</code> to display the
2783+ * <code>modelColumn</code>'th column of data in the model with a
2784+ * given <code>width</code>, <code>cellRenderer</code>,
2785+ * and <code>cellEditor</code> you can use:
2786+ * <pre>
2787+ *
2788+ * addColumn(new TableColumn(modelColumn, width, cellRenderer, cellEditor));
2789+ *
2790+ * </pre>
2791+ * [Any of the <code>TableColumn</code> constructors can be used
2792+ * instead of this one.]
2793+ * The model column number is stored inside the <code>TableColumn</code>
2794+ * and is used during rendering and editing to locate the appropriates
2795+ * data values in the model. The model column number does not change
2796+ * when columns are reordered in the view.
2797+ *
2798+ * @param aColumn the <code>TableColumn</code> to be added
2799+ * @see #removeColumn
2800+ */
2801+ public void addColumn(TableColumn aColumn) {
2802+ if (aColumn.getHeaderValue() == null) {
2803+ int modelColumn = aColumn.getModelIndex();
2804+ String columnName = getModel().getColumnName(modelColumn);
2805+ aColumn.setHeaderValue(columnName);
2806+ }
2807+ getColumnModel().addColumn(aColumn);
2808+ }
2809+
2810+ /**
2811+ * Removes <code>aColumn</code> from this <code>JTable</code>'s
2812+ * array of columns. Note: this method does not remove the column
2813+ * of data from the model; it just removes the <code>TableColumn</code>
2814+ * that was responsible for displaying it.
2815+ *
2816+ * @param aColumn the <code>TableColumn</code> to be removed
2817+ * @see #addColumn
2818+ */
2819+ public void removeColumn(TableColumn aColumn) {
2820+ getColumnModel().removeColumn(aColumn);
2821+ }
2822+
2823+ /**
2824+ * Moves the column <code>column</code> to the position currently
2825+ * occupied by the column <code>targetColumn</code> in the view.
2826+ * The old column at <code>targetColumn</code> is
2827+ * shifted left or right to make room.
2828+ *
2829+ * @param column the index of column to be moved
2830+ * @param targetColumn the new index of the column
2831+ */
2832+ public void moveColumn(int column, int targetColumn) {
2833+ getColumnModel().moveColumn(column, targetColumn);
2834+ }
2835+
2836+//
2837+// Cover methods for various models and helper methods
2838+//
2839+
2840+ /**
2841+ * Returns the index of the column that <code>point</code> lies in,
2842+ * or -1 if the result is not in the range
2843+ * [0, <code>getColumnCount()</code>-1].
2844+ *
2845+ * @param point the location of interest
2846+ * @return the index of the column that <code>point</code> lies in,
2847+ * or -1 if the result is not in the range
2848+ * [0, <code>getColumnCount()</code>-1]
2849+ * @see #rowAtPoint
2850+ */
2851+ public int columnAtPoint(Point point) {
2852+ int x = point.x;
2853+ if( !getComponentOrientation().isLeftToRight() ) {
2854+ x = getWidth() - x - 1;
2855+ }
2856+ return getColumnModel().getColumnIndexAtX(x);
2857+ }
2858+
2859+ /**
2860+ * Returns the index of the row that <code>point</code> lies in,
2861+ * or -1 if the result is not in the range
2862+ * [0, <code>getRowCount()</code>-1].
2863+ *
2864+ * @param point the location of interest
2865+ * @return the index of the row that <code>point</code> lies in,
2866+ * or -1 if the result is not in the range
2867+ * [0, <code>getRowCount()</code>-1]
2868+ * @see #columnAtPoint
2869+ */
2870+ public int rowAtPoint(Point point) {
2871+ int y = point.y;
2872+ int result = (rowModel == null) ? y/getRowHeight() : rowModel.getIndex(y);
2873+ if (result < 0) {
2874+ return -1;
2875+ }
2876+ else if (result >= getRowCount()) {
2877+ return -1;
2878+ }
2879+ else {
2880+ return result;
2881+ }
2882+ }
2883+
2884+ /**
2885+ * Returns a rectangle for the cell that lies at the intersection of
2886+ * <code>row</code> and <code>column</code>.
2887+ * If <code>includeSpacing</code> is true then the value returned
2888+ * has the full height and width of the row and column
2889+ * specified. If it is false, the returned rectangle is inset by the
2890+ * intercell spacing to return the true bounds of the rendering or
2891+ * editing component as it will be set during rendering.
2892+ * <p>
2893+ * If the column index is valid but the row index is less
2894+ * than zero the method returns a rectangle with the
2895+ * <code>y</code> and <code>height</code> values set appropriately
2896+ * and the <code>x</code> and <code>width</code> values both set
2897+ * to zero. In general, when either the row or column indices indicate a
2898+ * cell outside the appropriate range, the method returns a rectangle
2899+ * depicting the closest edge of the closest cell that is within
2900+ * the table's range. When both row and column indices are out
2901+ * of range the returned rectangle covers the closest
2902+ * point of the closest cell.
2903+ * <p>
2904+ * In all cases, calculations that use this method to calculate
2905+ * results along one axis will not fail because of anomalies in
2906+ * calculations along the other axis. When the cell is not valid
2907+ * the <code>includeSpacing</code> parameter is ignored.
2908+ *
2909+ * @param row the row index where the desired cell
2910+ * is located
2911+ * @param column the column index where the desired cell
2912+ * is located in the display; this is not
2913+ * necessarily the same as the column index
2914+ * in the data model for the table; the
2915+ * {@link #convertColumnIndexToView(int)}
2916+ * method may be used to convert a data
2917+ * model column index to a display
2918+ * column index
2919+ * @param includeSpacing if false, return the true cell bounds -
2920+ * computed by subtracting the intercell
2921+ * spacing from the height and widths of
2922+ * the column and row models
2923+ *
2924+ * @return the rectangle containing the cell at location
2925+ * <code>row</code>,<code>column</code>
2926+ * @see #getIntercellSpacing
2927+ */
2928+ public Rectangle getCellRect(int row, int column, boolean includeSpacing) {
2929+ Rectangle r = new Rectangle();
2930+ boolean valid = true;
2931+ if (row < 0) {
2932+ // y = height = 0;
2933+ valid = false;
2934+ }
2935+ else if (row >= getRowCount()) {
2936+ r.y = getHeight();
2937+ valid = false;
2938+ }
2939+ else {
2940+ r.height = getRowHeight(row);
2941+ r.y = (rowModel == null) ? row * r.height : rowModel.getPosition(row);
2942+ }
2943+
2944+ if (column < 0) {
2945+ if( !getComponentOrientation().isLeftToRight() ) {
2946+ r.x = getWidth();
2947+ }
2948+ // otherwise, x = width = 0;
2949+ valid = false;
2950+ }
2951+ else if (column >= getColumnCount()) {
2952+ if( getComponentOrientation().isLeftToRight() ) {
2953+ r.x = getWidth();
2954+ }
2955+ // otherwise, x = width = 0;
2956+ valid = false;
2957+ }
2958+ else {
2959+ TableColumnModel cm = getColumnModel();
2960+ if( getComponentOrientation().isLeftToRight() ) {
2961+ for(int i = 0; i < column; i++) {
2962+ r.x += cm.getColumn(i).getWidth();
2963+ }
2964+ } else {
2965+ for(int i = cm.getColumnCount()-1; i > column; i--) {
2966+ r.x += cm.getColumn(i).getWidth();
2967+ }
2968+ }
2969+ r.width = cm.getColumn(column).getWidth();
2970+ }
2971+
2972+ if (valid && !includeSpacing) {
2973+ // Bound the margins by their associated dimensions to prevent
2974+ // returning bounds with negative dimensions.
2975+ int rm = Math.min(getRowMargin(), r.height);
2976+ int cm = Math.min(getColumnModel().getColumnMargin(), r.width);
2977+ // This is not the same as grow(), it rounds differently.
2978+ r.setBounds(r.x + cm/2, r.y + rm/2, r.width - cm, r.height - rm);
2979+ }
2980+ return r;
2981+ }
2982+
2983+ private int viewIndexForColumn(TableColumn aColumn) {
2984+ TableColumnModel cm = getColumnModel();
2985+ for (int column = 0; column < cm.getColumnCount(); column++) {
2986+ if (cm.getColumn(column) == aColumn) {
2987+ return column;
2988+ }
2989+ }
2990+ return -1;
2991+ }
2992+
2993+ /**
2994+ * Causes this table to lay out its rows and columns. Overridden so
2995+ * that columns can be resized to accomodate a change in the size of
2996+ * a containing parent.
2997+ * Resizes one or more of the columns in the table
2998+ * so that the total width of all of this <code>JTable</code>'s
2999+ * columns is equal to the width of the table.
3000+ * <p>
3001+ * Before the layout begins the method gets the
3002+ * <code>resizingColumn</code> of the <code>tableHeader</code>.
3003+ * When the method is called as a result of the resizing of an enclosing window,
3004+ * the <code>resizingColumn</code> is <code>null</code>. This means that resizing
3005+ * has taken place "outside" the <code>JTable</code> and the change -
3006+ * or "delta" - should be distributed to all of the columns regardless
3007+ * of this <code>JTable</code>'s automatic resize mode.
3008+ * <p>
3009+ * If the <code>resizingColumn</code> is not <code>null</code>, it is one of
3010+ * the columns in the table that has changed size rather than
3011+ * the table itself. In this case the auto-resize modes govern
3012+ * the way the extra (or deficit) space is distributed
3013+ * amongst the available columns.
3014+ * <p>
3015+ * The modes are:
3016+ * <ul>
3017+ * <li> AUTO_RESIZE_OFF: Don't automatically adjust the column's
3018+ * widths at all. Use a horizontal scrollbar to accomodate the
3019+ * columns when their sum exceeds the width of the
3020+ * <code>Viewport</code>. If the <code>JTable</code> is not
3021+ * enclosed in a <code>JScrollPane</code> this may
3022+ * leave parts of the table invisible.
3023+ * <li> AUTO_RESIZE_NEXT_COLUMN: Use just the column after the
3024+ * resizing column. This results in the "boundary" or divider
3025+ * between adjacent cells being independently adjustable.
3026+ * <li> AUTO_RESIZE_SUBSEQUENT_COLUMNS: Use all columns after the
3027+ * one being adjusted to absorb the changes. This is the
3028+ * default behavior.
3029+ * <li> AUTO_RESIZE_LAST_COLUMN: Automatically adjust the
3030+ * size of the last column only. If the bounds of the last column
3031+ * prevent the desired size from being allocated, set the
3032+ * width of the last column to the appropriate limit and make
3033+ * no further adjustments.
3034+ * <li> AUTO_RESIZE_ALL_COLUMNS: Spread the delta amongst all the columns
3035+ * in the <code>JTable</code>, including the one that is being
3036+ * adjusted.
3037+ * </ul>
3038+ * <p>
3039+ * <bold>Note:</bold> When a <code>JTable</code> makes adjustments
3040+ * to the widths of the columns it respects their minimum and
3041+ * maximum values absolutely. It is therefore possible that,
3042+ * even after this method is called, the total width of the columns
3043+ * is still not equal to the width of the table. When this happens
3044+ * the <code>JTable</code> does not put itself
3045+ * in AUTO_RESIZE_OFF mode to bring up a scroll bar, or break other
3046+ * commitments of its current auto-resize mode -- instead it
3047+ * allows its bounds to be set larger (or smaller) than the total of the
3048+ * column minimum or maximum, meaning, either that there
3049+ * will not be enough room to display all of the columns, or that the
3050+ * columns will not fill the <code>JTable</code>'s bounds.
3051+ * These respectively, result in the clipping of some columns
3052+ * or an area being painted in the <code>JTable</code>'s
3053+ * background color during painting.
3054+ * <p>
3055+ * The mechanism for distributing the delta amongst the available
3056+ * columns is provided in a private method in the <code>JTable</code>
3057+ * class:
3058+ * <pre>
3059+ * adjustSizes(long targetSize, final Resizable3 r, boolean inverse)
3060+ * </pre>
3061+ * an explanation of which is provided in the following section.
3062+ * <code>Resizable3</code> is a private
3063+ * interface that allows any data structure containing a collection
3064+ * of elements with a size, preferred size, maximum size and minimum size
3065+ * to have its elements manipulated by the algorithm.
3066+ * <p>
3067+ * <H3> Distributing the delta </H3>
3068+ * <p>
3069+ * <H4> Overview </H4>
3070+ * <P>
3071+ * Call "DELTA" the difference between the target size and the
3072+ * sum of the preferred sizes of the elements in r. The individual
3073+ * sizes are calculated by taking the original preferred
3074+ * sizes and adding a share of the DELTA - that share being based on
3075+ * how far each preferred size is from its limiting bound (minimum or
3076+ * maximum).
3077+ * <p>
3078+ * <H4>Definition</H4>
3079+ * <P>
3080+ * Call the individual constraints min[i], max[i], and pref[i].
3081+ * <p>
3082+ * Call their respective sums: MIN, MAX, and PREF.
3083+ * <p>
3084+ * Each new size will be calculated using:
3085+ * <p>
3086+ * <pre>
3087+ * size[i] = pref[i] + delta[i]
3088+ * </pre>
3089+ * where each individual delta[i] is calculated according to:
3090+ * <p>
3091+ * If (DELTA < 0) we are in shrink mode where:
3092+ * <p>
3093+ * <PRE>
3094+ * DELTA
3095+ * delta[i] = ------------ * (pref[i] - min[i])
3096+ * (PREF - MIN)
3097+ * </PRE>
3098+ * If (DELTA > 0) we are in expand mode where:
3099+ * <p>
3100+ * <PRE>
3101+ * DELTA
3102+ * delta[i] = ------------ * (max[i] - pref[i])
3103+ * (MAX - PREF)
3104+ * </PRE>
3105+ * <P>
3106+ * The overall effect is that the total size moves that same percentage,
3107+ * k, towards the total minimum or maximum and that percentage guarantees
3108+ * accomodation of the required space, DELTA.
3109+ *
3110+ * <H4>Details</H4>
3111+ * <P>
3112+ * Naive evaluation of the formulae presented here would be subject to
3113+ * the aggregated rounding errors caused by doing this operation in finite
3114+ * precision (using ints). To deal with this, the multiplying factor above,
3115+ * is constantly recalculated and this takes account of the rounding
3116+ * errors in the previous iterations. The result is an algorithm that
3117+ * produces a set of integers whose values exactly sum to the supplied
3118+ * <code>targetSize</code>, and does so by spreading the rounding
3119+ * errors evenly over the given elements.
3120+ *
3121+ * <H4>When the MAX and MIN bounds are hit</H4>
3122+ * <P>
3123+ * When <code>targetSize</code> is outside the [MIN, MAX] range,
3124+ * the algorithm sets all sizes to their appropriate limiting value
3125+ * (maximum or minimum).
3126+ *
3127+ */
3128+ public void doLayout() {
3129+ TableColumn resizingColumn = getResizingColumn();
3130+ if (resizingColumn == null) {
3131+ setWidthsFromPreferredWidths(false);
3132+ }
3133+ else {
3134+ // JTable behaves like a layout manger - but one in which the
3135+ // user can come along and dictate how big one of the children
3136+ // (columns) is supposed to be.
3137+
3138+ // A column has been resized and JTable may need to distribute
3139+ // any overall delta to other columns, according to the resize mode.
3140+ int columnIndex = viewIndexForColumn(resizingColumn);
3141+ int delta = getWidth() - getColumnModel().getTotalColumnWidth();
3142+ accommodateDelta(columnIndex, delta);
3143+ delta = getWidth() - getColumnModel().getTotalColumnWidth();
3144+
3145+ // If the delta cannot be completely accomodated, then the
3146+ // resizing column will have to take any remainder. This means
3147+ // that the column is not being allowed to take the requested
3148+ // width. This happens under many circumstances: For example,
3149+ // AUTO_RESIZE_NEXT_COLUMN specifies that any delta be distributed
3150+ // to the column after the resizing column. If one were to attempt
3151+ // to resize the last column of the table, there would be no
3152+ // columns after it, and hence nowhere to distribute the delta.
3153+ // It would then be given entirely back to the resizing column,
3154+ // preventing it from changing size.
3155+ if (delta != 0) {
3156+ resizingColumn.setWidth(resizingColumn.getWidth() + delta);
3157+ }
3158+
3159+ // At this point the JTable has to work out what preferred sizes
3160+ // would have resulted in the layout the user has chosen.
3161+ // Thereafter, during window resizing etc. it has to work off
3162+ // the preferred sizes as usual - the idea being that, whatever
3163+ // the user does, everything stays in synch and things don't jump
3164+ // around.
3165+ setWidthsFromPreferredWidths(true);
3166+ }
3167+
3168+ super.doLayout();
3169+ }
3170+
3171+ private TableColumn getResizingColumn() {
3172+ return (tableHeader == null) ? null
3173+ : tableHeader.getResizingColumn();
3174+ }
3175+
3176+ /**
3177+ * Sizes the table columns to fit the available space.
3178+ * @deprecated As of Swing version 1.0.3,
3179+ * replaced by <code>doLayout()</code>.
3180+ * @see #doLayout
3181+ */
3182+ @Deprecated
3183+ public void sizeColumnsToFit(boolean lastColumnOnly) {
3184+ int oldAutoResizeMode = autoResizeMode;
3185+ setAutoResizeMode(lastColumnOnly ? AUTO_RESIZE_LAST_COLUMN
3186+ : AUTO_RESIZE_ALL_COLUMNS);
3187+ sizeColumnsToFit(-1);
3188+ setAutoResizeMode(oldAutoResizeMode);
3189+ }
3190+
3191+ /**
3192+ * Obsolete as of Java 2 platform v1.4. Please use the
3193+ * <code>doLayout()</code> method instead.
3194+ * @param resizingColumn the column whose resizing made this adjustment
3195+ * necessary or -1 if there is no such column
3196+ * @see #doLayout
3197+ */
3198+ public void sizeColumnsToFit(int resizingColumn) {
3199+ if (resizingColumn == -1) {
3200+ setWidthsFromPreferredWidths(false);
3201+ }
3202+ else {
3203+ if (autoResizeMode == AUTO_RESIZE_OFF) {
3204+ TableColumn aColumn = getColumnModel().getColumn(resizingColumn);
3205+ aColumn.setPreferredWidth(aColumn.getWidth());
3206+ }
3207+ else {
3208+ int delta = getWidth() - getColumnModel().getTotalColumnWidth();
3209+ accommodateDelta(resizingColumn, delta);
3210+ setWidthsFromPreferredWidths(true);
3211+ }
3212+ }
3213+ }
3214+
3215+ private void setWidthsFromPreferredWidths(final boolean inverse) {
3216+ int totalWidth = getWidth();
3217+ int totalPreferred = getPreferredSize().width;
3218+ int target = !inverse ? totalWidth : totalPreferred;
3219+
3220+ final TableColumnModel cm = columnModel;
3221+ Resizable3 r = new Resizable3() {
3222+ public int getElementCount() { return cm.getColumnCount(); }
3223+ public int getLowerBoundAt(int i) { return cm.getColumn(i).getMinWidth(); }
3224+ public int getUpperBoundAt(int i) { return cm.getColumn(i).getMaxWidth(); }
3225+ public int getMidPointAt(int i) {
3226+ if (!inverse) {
3227+ return cm.getColumn(i).getPreferredWidth();
3228+ }
3229+ else {
3230+ return cm.getColumn(i).getWidth();
3231+ }
3232+ }
3233+ public void setSizeAt(int s, int i) {
3234+ if (!inverse) {
3235+ cm.getColumn(i).setWidth(s);
3236+ }
3237+ else {
3238+ cm.getColumn(i).setPreferredWidth(s);
3239+ }
3240+ }
3241+ };
3242+
3243+ adjustSizes(target, r, inverse);
3244+ }
3245+
3246+
3247+ // Distribute delta over columns, as indicated by the autoresize mode.
3248+ private void accommodateDelta(int resizingColumnIndex, int delta) {
3249+ int columnCount = getColumnCount();
3250+ int from = resizingColumnIndex;
3251+ int to;
3252+
3253+ // Use the mode to determine how to absorb the changes.
3254+ switch(autoResizeMode) {
3255+ case AUTO_RESIZE_NEXT_COLUMN:
3256+ from = from + 1;
3257+ to = Math.min(from + 1, columnCount); break;
3258+ case AUTO_RESIZE_SUBSEQUENT_COLUMNS:
3259+ from = from + 1;
3260+ to = columnCount; break;
3261+ case AUTO_RESIZE_LAST_COLUMN:
3262+ from = columnCount - 1;
3263+ to = from + 1; break;
3264+ case AUTO_RESIZE_ALL_COLUMNS:
3265+ from = 0;
3266+ to = columnCount; break;
3267+ default:
3268+ return;
3269+ }
3270+
3271+ final int start = from;
3272+ final int end = to;
3273+ final TableColumnModel cm = columnModel;
3274+ Resizable3 r = new Resizable3() {
3275+ public int getElementCount() { return end-start; }
3276+ public int getLowerBoundAt(int i) { return cm.getColumn(i+start).getMinWidth(); }
3277+ public int getUpperBoundAt(int i) { return cm.getColumn(i+start).getMaxWidth(); }
3278+ public int getMidPointAt(int i) { return cm.getColumn(i+start).getWidth(); }
3279+ public void setSizeAt(int s, int i) { cm.getColumn(i+start).setWidth(s); }
3280+ };
3281+
3282+ int totalWidth = 0;
3283+ for(int i = from; i < to; i++) {
3284+ TableColumn aColumn = columnModel.getColumn(i);
3285+ int input = aColumn.getWidth();
3286+ totalWidth = totalWidth + input;
3287+ }
3288+
3289+ adjustSizes(totalWidth + delta, r, false);
3290+ }
3291+
3292+ private interface Resizable2 {
3293+ public int getElementCount();
3294+ public int getLowerBoundAt(int i);
3295+ public int getUpperBoundAt(int i);
3296+ public void setSizeAt(int newSize, int i);
3297+ }
3298+
3299+ private interface Resizable3 extends Resizable2 {
3300+ public int getMidPointAt(int i);
3301+ }
3302+
3303+
3304+ private void adjustSizes(long target, final Resizable3 r, boolean inverse) {
3305+ int N = r.getElementCount();
3306+ long totalPreferred = 0;
3307+ for(int i = 0; i < N; i++) {
3308+ totalPreferred += r.getMidPointAt(i);
3309+ }
3310+ Resizable2 s;
3311+ if ((target < totalPreferred) == !inverse) {
3312+ s = new Resizable2() {
3313+ public int getElementCount() { return r.getElementCount(); }
3314+ public int getLowerBoundAt(int i) { return r.getLowerBoundAt(i); }
3315+ public int getUpperBoundAt(int i) { return r.getMidPointAt(i); }
3316+ public void setSizeAt(int newSize, int i) { r.setSizeAt(newSize, i); }
3317+
3318+ };
3319+ }
3320+ else {
3321+ s = new Resizable2() {
3322+ public int getElementCount() { return r.getElementCount(); }
3323+ public int getLowerBoundAt(int i) { return r.getMidPointAt(i); }
3324+ public int getUpperBoundAt(int i) { return r.getUpperBoundAt(i); }
3325+ public void setSizeAt(int newSize, int i) { r.setSizeAt(newSize, i); }
3326+
3327+ };
3328+ }
3329+ adjustSizes(target, s, !inverse);
3330+ }
3331+
3332+ private void adjustSizes(long target, Resizable2 r, boolean limitToRange) {
3333+ long totalLowerBound = 0;
3334+ long totalUpperBound = 0;
3335+ for(int i = 0; i < r.getElementCount(); i++) {
3336+ totalLowerBound += r.getLowerBoundAt(i);
3337+ totalUpperBound += r.getUpperBoundAt(i);
3338+ }
3339+
3340+ if (limitToRange) {
3341+ target = Math.min(Math.max(totalLowerBound, target), totalUpperBound);
3342+ }
3343+
3344+ for(int i = 0; i < r.getElementCount(); i++) {
3345+ int lowerBound = r.getLowerBoundAt(i);
3346+ int upperBound = r.getUpperBoundAt(i);
3347+ // Check for zero. This happens when the distribution of the delta
3348+ // finishes early due to a series of "fixed" entries at the end.
3349+ // In this case, lowerBound == upperBound, for all subsequent terms.
3350+ int newSize;
3351+ if (totalLowerBound == totalUpperBound) {
3352+ newSize = lowerBound;
3353+ }
3354+ else {
3355+ double f = (double)(target - totalLowerBound)/(totalUpperBound - totalLowerBound);
3356+ newSize = (int)Math.round(lowerBound+f*(upperBound - lowerBound));
3357+ // We'd need to round manually in an all integer version.
3358+ // size[i] = (int)(((totalUpperBound - target) * lowerBound +
3359+ // (target - totalLowerBound) * upperBound)/(totalUpperBound-totalLowerBound));
3360+ }
3361+ r.setSizeAt(newSize, i);
3362+ target -= newSize;
3363+ totalLowerBound -= lowerBound;
3364+ totalUpperBound -= upperBound;
3365+ }
3366+ }
3367+
3368+ /**
3369+ * Overrides <code>JComponent</code>'s <code>getToolTipText</code>
3370+ * method in order to allow the renderer's tips to be used
3371+ * if it has text set.
3372+ * <p>
3373+ * <bold>Note:</bold> For <code>JTable</code> to properly display
3374+ * tooltips of its renderers
3375+ * <code>JTable</code> must be a registered component with the
3376+ * <code>ToolTipManager</code>.
3377+ * This is done automatically in <code>initializeLocalVars</code>,
3378+ * but if at a later point <code>JTable</code> is told
3379+ * <code>setToolTipText(null)</code> it will unregister the table
3380+ * component, and no tips from renderers will display anymore.
3381+ *
3382+ * @see JComponent#getToolTipText
3383+ */
3384+ public String getToolTipText(MouseEvent event) {
3385+ String tip = null;
3386+ Point p = event.getPoint();
3387+
3388+ // Locate the renderer under the event location
3389+ int hitColumnIndex = columnAtPoint(p);
3390+ int hitRowIndex = rowAtPoint(p);
3391+
3392+ if ((hitColumnIndex != -1) && (hitRowIndex != -1)) {
3393+ TableCellRenderer renderer = getCellRenderer(hitRowIndex, hitColumnIndex);
3394+ Component component = prepareRenderer(renderer, hitRowIndex, hitColumnIndex);
3395+
3396+ // Now have to see if the component is a JComponent before
3397+ // getting the tip
3398+ if (component instanceof JComponent) {
3399+ // Convert the event to the renderer's coordinate system
3400+ Rectangle cellRect = getCellRect(hitRowIndex, hitColumnIndex, false);
3401+ p.translate(-cellRect.x, -cellRect.y);
3402+ MouseEvent newEvent = new MouseEvent(component, event.getID(),
3403+ event.getWhen(), event.getModifiers(),
3404+ p.x, p.y,
3405+ event.getXOnScreen(),
3406+ event.getYOnScreen(),
3407+ event.getClickCount(),
3408+ event.isPopupTrigger(),
3409+ MouseEvent.NOBUTTON);
3410+
3411+ tip = ((JComponent)component).getToolTipText(newEvent);
3412+ }
3413+ }
3414+
3415+ // No tip from the renderer get our own tip
3416+ if (tip == null)
3417+ tip = getToolTipText();
3418+
3419+ return tip;
3420+ }
3421+
3422+//
3423+// Editing Support
3424+//
3425+
3426+ /**
3427+ * Sets whether editors in this JTable get the keyboard focus
3428+ * when an editor is activated as a result of the JTable
3429+ * forwarding keyboard events for a cell.
3430+ * By default, this property is false, and the JTable
3431+ * retains the focus unless the cell is clicked.
3432+ *
3433+ * @param surrendersFocusOnKeystroke true if the editor should get the focus
3434+ * when keystrokes cause the editor to be
3435+ * activated
3436+ *
3437+ *
3438+ * @see #getSurrendersFocusOnKeystroke
3439+ * @since 1.4
3440+ */
3441+ public void setSurrendersFocusOnKeystroke(boolean surrendersFocusOnKeystroke) {
3442+ this.surrendersFocusOnKeystroke = surrendersFocusOnKeystroke;
3443+ }
3444+
3445+ /**
3446+ * Returns true if the editor should get the focus
3447+ * when keystrokes cause the editor to be activated
3448+ *
3449+ * @return true if the editor should get the focus
3450+ * when keystrokes cause the editor to be
3451+ * activated
3452+ *
3453+ * @see #setSurrendersFocusOnKeystroke
3454+ * @since 1.4
3455+ */
3456+ public boolean getSurrendersFocusOnKeystroke() {
3457+ return surrendersFocusOnKeystroke;
3458+ }
3459+
3460+ /**
3461+ * Programmatically starts editing the cell at <code>row</code> and
3462+ * <code>column</code>, if those indices are in the valid range, and
3463+ * the cell at those indices is editable.
3464+ * Note that this is a convenience method for
3465+ * <code>editCellAt(int, int, null)</code>.
3466+ *
3467+ * @param row the row to be edited
3468+ * @param column the column to be edited
3469+ * @return false if for any reason the cell cannot be edited,
3470+ * or if the indices are invalid
3471+ */
3472+ public boolean editCellAt(int row, int column) {
3473+ return editCellAt(row, column, null);
3474+ }
3475+
3476+ /**
3477+ * Programmatically starts editing the cell at <code>row</code> and
3478+ * <code>column</code>, if those indices are in the valid range, and
3479+ * the cell at those indices is editable.
3480+ * To prevent the <code>JTable</code> from
3481+ * editing a particular table, column or cell value, return false from
3482+ * the <code>isCellEditable</code> method in the <code>TableModel</code>
3483+ * interface.
3484+ *
3485+ * @param row the row to be edited
3486+ * @param column the column to be edited
3487+ * @param e event to pass into <code>shouldSelectCell</code>;
3488+ * note that as of Java 2 platform v1.2, the call to
3489+ * <code>shouldSelectCell</code> is no longer made
3490+ * @return false if for any reason the cell cannot be edited,
3491+ * or if the indices are invalid
3492+ */
3493+ public boolean editCellAt(int row, int column, EventObject e){
3494+ if (cellEditor != null && !cellEditor.stopCellEditing()) {
3495+ return false;
3496+ }
3497+
3498+ if (row < 0 || row >= getRowCount() ||
3499+ column < 0 || column >= getColumnCount()) {
3500+ return false;
3501+ }
3502+
3503+ if (!isCellEditable(row, column))
3504+ return false;
3505+
3506+ if (editorRemover == null) {
3507+ KeyboardFocusManager fm =
3508+ KeyboardFocusManager.getCurrentKeyboardFocusManager();
3509+ editorRemover = new CellEditorRemover(fm);
3510+ fm.addPropertyChangeListener("permanentFocusOwner", editorRemover);
3511+ }
3512+
3513+ TableCellEditor editor = getCellEditor(row, column);
3514+ if (editor != null && editor.isCellEditable(e)) {
3515+ editorComp = prepareEditor(editor, row, column);
3516+ if (editorComp == null) {
3517+ removeEditor();
3518+ return false;
3519+ }
3520+ editorComp.setBounds(getCellRect(row, column, false));
3521+ add(editorComp);
3522+ editorComp.validate();
3523+ editorComp.repaint();
3524+
3525+ setCellEditor(editor);
3526+ setEditingRow(row);
3527+ setEditingColumn(column);
3528+ editor.addCellEditorListener(this);
3529+
3530+ return true;
3531+ }
3532+ return false;
3533+ }
3534+
3535+ /**
3536+ * Returns true if a cell is being edited.
3537+ *
3538+ * @return true if the table is editing a cell
3539+ * @see #editingColumn
3540+ * @see #editingRow
3541+ */
3542+ public boolean isEditing() {
3543+ return cellEditor != null;
3544+ }
3545+
3546+ /**
3547+ * Returns the component that is handling the editing session.
3548+ * If nothing is being edited, returns null.
3549+ *
3550+ * @return Component handling editing session
3551+ */
3552+ public Component getEditorComponent() {
3553+ return editorComp;
3554+ }
3555+
3556+ /**
3557+ * Returns the index of the column that contains the cell currently
3558+ * being edited. If nothing is being edited, returns -1.
3559+ *
3560+ * @return the index of the column that contains the cell currently
3561+ * being edited; returns -1 if nothing being edited
3562+ * @see #editingRow
3563+ */
3564+ public int getEditingColumn() {
3565+ return editingColumn;
3566+ }
3567+
3568+ /**
3569+ * Returns the index of the row that contains the cell currently
3570+ * being edited. If nothing is being edited, returns -1.
3571+ *
3572+ * @return the index of the row that contains the cell currently
3573+ * being edited; returns -1 if nothing being edited
3574+ * @see #editingColumn
3575+ */
3576+ public int getEditingRow() {
3577+ return editingRow;
3578+ }
3579+
3580+//
3581+// Managing TableUI
3582+//
3583+
3584+ /**
3585+ * Returns the L&F object that renders this component.
3586+ *
3587+ * @return the <code>TableUI</code> object that renders this component
3588+ */
3589+ public TableUI getUI() {
3590+ return (TableUI)ui;
3591+ }
3592+
3593+ /**
3594+ * Sets the L&F object that renders this component and repaints.
3595+ *
3596+ * @param ui the TableUI L&F object
3597+ * @see UIDefaults#getUI
3598+ * @beaninfo
3599+ * bound: true
3600+ * hidden: true
3601+ * attribute: visualUpdate true
3602+ * description: The UI object that implements the Component's LookAndFeel.
3603+ */
3604+ public void setUI(TableUI ui) {
3605+ if (this.ui != ui) {
3606+ super.setUI(ui);
3607+ repaint();
3608+ }
3609+ }
3610+
3611+ /**
3612+ * Notification from the <code>UIManager</code> that the L&F has changed.
3613+ * Replaces the current UI object with the latest version from the
3614+ * <code>UIManager</code>.
3615+ *
3616+ * @see JComponent#updateUI
3617+ */
3618+ public void updateUI() {
3619+ // Update the UIs of the cell renderers, cell editors and header renderers.
3620+ TableColumnModel cm = getColumnModel();
3621+ for(int column = 0; column < cm.getColumnCount(); column++) {
3622+ TableColumn aColumn = cm.getColumn(column);
3623+ SwingUtilities.updateRendererOrEditorUI(aColumn.getCellRenderer());
3624+ SwingUtilities.updateRendererOrEditorUI(aColumn.getCellEditor());
3625+ SwingUtilities.updateRendererOrEditorUI(aColumn.getHeaderRenderer());
3626+ }
3627+
3628+ // Update the UIs of all the default renderers.
3629+ Enumeration defaultRenderers = defaultRenderersByColumnClass.elements();
3630+ while (defaultRenderers.hasMoreElements()) {
3631+ SwingUtilities.updateRendererOrEditorUI(defaultRenderers.nextElement());
3632+ }
3633+
3634+ // Update the UIs of all the default editors.
3635+ Enumeration defaultEditors = defaultEditorsByColumnClass.elements();
3636+ while (defaultEditors.hasMoreElements()) {
3637+ SwingUtilities.updateRendererOrEditorUI(defaultEditors.nextElement());
3638+ }
3639+
3640+ // Update the UI of the table header
3641+ if (tableHeader != null && tableHeader.getParent() == null) {
3642+ tableHeader.updateUI();
3643+ }
3644+
3645+ // Update UI applied to parent ScrollPane
3646+ configureEnclosingScrollPaneUI();
3647+
3648+ setUI((TableUI)UIManager.getUI(this));
3649+ }
3650+
3651+ /**
3652+ * Returns the suffix used to construct the name of the L&F class used to
3653+ * render this component.
3654+ *
3655+ * @return the string "TableUI"
3656+ * @see JComponent#getUIClassID
3657+ * @see UIDefaults#getUI
3658+ */
3659+ public String getUIClassID() {
3660+ return uiClassID;
3661+ }
3662+
3663+
3664+//
3665+// Managing models
3666+//
3667+
3668+ /**
3669+ * Sets the data model for this table to <code>newModel</code> and registers
3670+ * with it for listener notifications from the new data model.
3671+ *
3672+ * @param dataModel the new data source for this table
3673+ * @exception IllegalArgumentException if <code>newModel</code> is <code>null</code>
3674+ * @see #getModel
3675+ * @beaninfo
3676+ * bound: true
3677+ * description: The model that is the source of the data for this view.
3678+ */
3679+ public void setModel(TableModel dataModel) {
3680+ if (dataModel == null) {
3681+ throw new IllegalArgumentException("Cannot set a null TableModel");
3682+ }
3683+ if (this.dataModel != dataModel) {
3684+ TableModel old = this.dataModel;
3685+ if (old != null) {
3686+ old.removeTableModelListener(this);
3687+ }
3688+ this.dataModel = dataModel;
3689+ dataModel.addTableModelListener(this);
3690+
3691+ tableChanged(new TableModelEvent(dataModel, TableModelEvent.HEADER_ROW));
3692+
3693+ firePropertyChange("model", old, dataModel);
3694+
3695+ if (getAutoCreateRowSorter()) {
3696+ setRowSorter(new TableRowSorter<TableModel>(dataModel));
3697+ }
3698+ }
3699+ }
3700+
3701+ /**
3702+ * Returns the <code>TableModel</code> that provides the data displayed by this
3703+ * <code>JTable</code>.
3704+ *
3705+ * @return the <code>TableModel</code> that provides the data displayed by this <code>JTable</code>
3706+ * @see #setModel
3707+ */
3708+ public TableModel getModel() {
3709+ return dataModel;
3710+ }
3711+
3712+ /**
3713+ * Sets the column model for this table to <code>newModel</code> and registers
3714+ * for listener notifications from the new column model. Also sets
3715+ * the column model of the <code>JTableHeader</code> to <code>columnModel</code>.
3716+ *
3717+ * @param columnModel the new data source for this table
3718+ * @exception IllegalArgumentException if <code>columnModel</code> is <code>null</code>
3719+ * @see #getColumnModel
3720+ * @beaninfo
3721+ * bound: true
3722+ * description: The object governing the way columns appear in the view.
3723+ */
3724+ public void setColumnModel(TableColumnModel columnModel) {
3725+ if (columnModel == null) {
3726+ throw new IllegalArgumentException("Cannot set a null ColumnModel");
3727+ }
3728+ TableColumnModel old = this.columnModel;
3729+ if (columnModel != old) {
3730+ if (old != null) {
3731+ old.removeColumnModelListener(this);
3732+ }
3733+ this.columnModel = columnModel;
3734+ columnModel.addColumnModelListener(this);
3735+
3736+ // Set the column model of the header as well.
3737+ if (tableHeader != null) {
3738+ tableHeader.setColumnModel(columnModel);
3739+ }
3740+
3741+ firePropertyChange("columnModel", old, columnModel);
3742+ resizeAndRepaint();
3743+ }
3744+ }
3745+
3746+ /**
3747+ * Returns the <code>TableColumnModel</code> that contains all column information
3748+ * of this table.
3749+ *
3750+ * @return the object that provides the column state of the table
3751+ * @see #setColumnModel
3752+ */
3753+ public TableColumnModel getColumnModel() {
3754+ return columnModel;
3755+ }
3756+
3757+ /**
3758+ * Sets the row selection model for this table to <code>newModel</code>
3759+ * and registers for listener notifications from the new selection model.
3760+ *
3761+ * @param newModel the new selection model
3762+ * @exception IllegalArgumentException if <code>newModel</code> is <code>null</code>
3763+ * @see #getSelectionModel
3764+ * @beaninfo
3765+ * bound: true
3766+ * description: The selection model for rows.
3767+ */
3768+ public void setSelectionModel(ListSelectionModel newModel) {
3769+ if (newModel == null) {
3770+ throw new IllegalArgumentException("Cannot set a null SelectionModel");
3771+ }
3772+
3773+ ListSelectionModel oldModel = selectionModel;
3774+
3775+ if (newModel != oldModel) {
3776+ if (oldModel != null) {
3777+ oldModel.removeListSelectionListener(this);
3778+ }
3779+
3780+ selectionModel = newModel;
3781+ newModel.addListSelectionListener(this);
3782+
3783+ firePropertyChange("selectionModel", oldModel, newModel);
3784+ repaint();
3785+ }
3786+ }
3787+
3788+ /**
3789+ * Returns the <code>ListSelectionModel</code> that is used to maintain row
3790+ * selection state.
3791+ *
3792+ * @return the object that provides row selection state, <code>null</code>
3793+ * if row selection is not allowed
3794+ * @see #setSelectionModel
3795+ */
3796+ public ListSelectionModel getSelectionModel() {
3797+ return selectionModel;
3798+ }
3799+
3800+//
3801+// RowSorterListener
3802+//
3803+
3804+ /**
3805+ * <code>RowSorterListener</code> notification that the
3806+ * <code>RowSorter</code> has changed in some way.
3807+ *
3808+ * @param e the <code>RowSorterEvent</code> describing the change
3809+ * @throws NullPointerException if <code>e</code> is <code>null</code>
3810+ * @since 1.6
3811+ */
3812+ public void sorterChanged(RowSorterEvent e) {
3813+ if (e.getType() == RowSorterEvent.Type.SORT_ORDER_CHANGED) {
3814+ JTableHeader header = getTableHeader();
3815+ if (header != null) {
3816+ header.repaint();
3817+ }
3818+ }
3819+ else if (e.getType() == RowSorterEvent.Type.SORTED) {
3820+ sorterChanged = true;
3821+ if (!ignoreSortChange) {
3822+ sortedTableChanged(e, null);
3823+ }
3824+ }
3825+ }
3826+
3827+
3828+ /**
3829+ * SortManager provides support for managing the selection and variable
3830+ * row heights when sorting is enabled. This information is encapsulated
3831+ * into a class to avoid bulking up JTable.
3832+ */
3833+ private final class SortManager {
3834+ RowSorter<? extends TableModel> sorter;
3835+
3836+ // Selection, in terms of the model. This is lazily created
3837+ // as needed.
3838+ private ListSelectionModel modelSelection;
3839+ private int modelLeadIndex;
3840+ // Set to true while in the process of changing the selection.
3841+ // If this is true the selection change is ignored.
3842+ private boolean syncingSelection;
3843+ // Temporary cache of selection, in terms of model. This is only used
3844+ // if we don't need the full weight of modelSelection.
3845+ private int[] lastModelSelection;
3846+
3847+ // Heights of the rows in terms of the model.
3848+ private SizeSequence modelRowSizes;
3849+
3850+
3851+ SortManager(RowSorter<? extends TableModel> sorter) {
3852+ this.sorter = sorter;
3853+ sorter.addRowSorterListener(JTable.this);
3854+ }
3855+
3856+ /**
3857+ * Disposes any resources used by this SortManager.
3858+ */
3859+ public void dispose() {
3860+ if (sorter != null) {
3861+ sorter.removeRowSorterListener(JTable.this);
3862+ }
3863+ }
3864+
3865+ /**
3866+ * Sets the height for a row at a specified index.
3867+ */
3868+ public void setViewRowHeight(int viewIndex, int rowHeight) {
3869+ if (modelRowSizes == null) {
3870+ modelRowSizes = new SizeSequence(getModel().getRowCount(),
3871+ getRowHeight());
3872+ }
3873+ modelRowSizes.setSize(convertRowIndexToModel(viewIndex),rowHeight);
3874+ }
3875+
3876+ /**
3877+ * Invoked when the underlying model has completely changed.
3878+ */
3879+ public void allChanged() {
3880+ modelLeadIndex = -1;
3881+ modelSelection = null;
3882+ modelRowSizes = null;
3883+ }
3884+
3885+ /**
3886+ * Invoked when the selection, on the view, has changed.
3887+ */
3888+ public void viewSelectionChanged(ListSelectionEvent e) {
3889+ if (!syncingSelection && modelSelection != null) {
3890+ modelSelection = null;
3891+ }
3892+ }
3893+
3894+ /**
3895+ * Invoked when either the table model has changed, or the RowSorter
3896+ * has changed. This is invoked prior to notifying the sorter of the
3897+ * change.
3898+ */
3899+ public void prepareForChange(RowSorterEvent sortEvent,
3900+ ModelChange change) {
3901+ if (getUpdateSelectionOnSort()) {
3902+ cacheSelection(sortEvent, change);
3903+ }
3904+ }
3905+
3906+ /**
3907+ * Updates the internal cache of the selection based on the change.
3908+ */
3909+ private void cacheSelection(RowSorterEvent sortEvent,
3910+ ModelChange change) {
3911+ if (sortEvent != null) {
3912+ // sort order changed. If modelSelection is null and filtering
3913+ // is enabled we need to cache the selection in terms of the
3914+ // underlying model, this will allow us to correctly restore
3915+ // the selection even if rows are filtered out.
3916+ if (modelSelection == null &&
3917+ sorter.getViewRowCount() != getModel().getRowCount()) {
3918+ modelSelection = new DefaultListSelectionModel();
3919+ ListSelectionModel viewSelection = getSelectionModel();
3920+ int min = viewSelection.getMinSelectionIndex();
3921+ int max = viewSelection.getMaxSelectionIndex();
3922+ int modelIndex;
3923+ for (int viewIndex = min; viewIndex <= max; viewIndex++) {
3924+ if (viewSelection.isSelectedIndex(viewIndex)) {
3925+ modelIndex = convertRowIndexToModel(
3926+ sortEvent, viewIndex);
3927+ if (modelIndex != -1) {
3928+ modelSelection.addSelectionInterval(
3929+ modelIndex, modelIndex);
3930+ }
3931+ }
3932+ }
3933+ modelIndex = convertRowIndexToModel(sortEvent,
3934+ viewSelection.getLeadSelectionIndex());
3935+ SwingUtilities2.setLeadAnchorWithoutSelection(
3936+ modelSelection, modelIndex, modelIndex);
3937+ } else if (modelSelection == null) {
3938+ // Sorting changed, haven't cached selection in terms
3939+ // of model and no filtering. Temporarily cache selection.
3940+ cacheModelSelection(sortEvent);
3941+ }
3942+ } else if (change.allRowsChanged) {
3943+ // All the rows have changed, chuck any cached selection.
3944+ modelSelection = null;
3945+ } else if (modelSelection != null) {
3946+ // Table changed, reflect changes in cached selection model.
3947+ switch(change.type) {
3948+ case TableModelEvent.DELETE:
3949+ modelSelection.removeIndexInterval(change.startModelIndex,
3950+ change.endModelIndex);
3951+ break;
3952+ case TableModelEvent.INSERT:
3953+ modelSelection.insertIndexInterval(change.startModelIndex,
3954+ change.endModelIndex,
3955+ true);
3956+ break;
3957+ default:
3958+ break;
3959+ }
3960+ } else {
3961+ // table changed, but haven't cached rows, temporarily
3962+ // cache them.
3963+ cacheModelSelection(null);
3964+ }
3965+ }
3966+
3967+ private void cacheModelSelection(RowSorterEvent sortEvent) {
3968+ lastModelSelection = convertSelectionToModel(sortEvent);
3969+ modelLeadIndex = convertRowIndexToModel(sortEvent,
3970+ selectionModel.getLeadSelectionIndex());
3971+ }
3972+
3973+ /**
3974+ * Inovked when either the table has changed or the sorter has changed
3975+ * and after the sorter has been notified. If necessary this will
3976+ * reapply the selection and variable row heights.
3977+ */
3978+ public void processChange(RowSorterEvent sortEvent,
3979+ ModelChange change,
3980+ boolean sorterChanged) {
3981+ if (change != null) {
3982+ if (change.allRowsChanged) {
3983+ modelRowSizes = null;
3984+ rowModel = null;
3985+ } else if (modelRowSizes != null) {
3986+ if (change.type == TableModelEvent.INSERT) {
3987+ modelRowSizes.insertEntries(change.startModelIndex,
3988+ change.endModelIndex -
3989+ change.startModelIndex + 1,
3990+ getRowHeight());
3991+ } else if (change.type == TableModelEvent.DELETE) {
3992+ modelRowSizes.removeEntries(change.startModelIndex,
3993+ change.endModelIndex -
3994+ change.startModelIndex +1 );
3995+ }
3996+ }
3997+ }
3998+ if (sorterChanged) {
3999+ setViewRowHeightsFromModel();
4000+ restoreSelection(change);
4001+ }
4002+ }
4003+
4004+ /**
4005+ * Resets the variable row heights in terms of the view from
4006+ * that of the variable row heights in terms of the model.
4007+ */
4008+ private void setViewRowHeightsFromModel() {
4009+ if (modelRowSizes != null) {
4010+ rowModel.setSizes(getRowCount(), getRowHeight());
4011+ for (int viewIndex = getRowCount() - 1; viewIndex >= 0;
4012+ viewIndex--) {
4013+ int modelIndex = convertRowIndexToModel(viewIndex);
4014+ rowModel.setSize(viewIndex,
4015+ modelRowSizes.getSize(modelIndex));
4016+ }
4017+ }
4018+ }
4019+
4020+ /**
4021+ * Restores the selection from that in terms of the model.
4022+ */
4023+ private void restoreSelection(ModelChange change) {
4024+ syncingSelection = true;
4025+ if (lastModelSelection != null) {
4026+ restoreSortingSelection(lastModelSelection,
4027+ modelLeadIndex, change);
4028+ lastModelSelection = null;
4029+ } else if (modelSelection != null) {
4030+ ListSelectionModel viewSelection = getSelectionModel();
4031+ viewSelection.setValueIsAdjusting(true);
4032+ viewSelection.clearSelection();
4033+ int min = modelSelection.getMinSelectionIndex();
4034+ int max = modelSelection.getMaxSelectionIndex();
4035+ int viewIndex;
4036+ for (int modelIndex = min; modelIndex <= max; modelIndex++) {
4037+ if (modelSelection.isSelectedIndex(modelIndex)) {
4038+ viewIndex = convertRowIndexToView(modelIndex);
4039+ if (viewIndex != -1) {
4040+ viewSelection.addSelectionInterval(viewIndex,
4041+ viewIndex);
4042+ }
4043+ }
4044+ }
4045+ // Restore the lead
4046+ int viewLeadIndex = modelSelection.getLeadSelectionIndex();
4047+ if (viewLeadIndex != -1) {
4048+ viewLeadIndex = convertRowIndexToView(viewLeadIndex);
4049+ }
4050+ SwingUtilities2.setLeadAnchorWithoutSelection(
4051+ viewSelection, viewLeadIndex, viewLeadIndex);
4052+ viewSelection.setValueIsAdjusting(false);
4053+ }
4054+ syncingSelection = false;
4055+ }
4056+ }
4057+
4058+
4059+ /**
4060+ * ModelChange is used when sorting to restore state, it corresponds
4061+ * to data from a TableModelEvent. The values are precalculated as
4062+ * they are used extensively.
4063+ */
4064+ private final class ModelChange {
4065+ // Starting index of the change, in terms of the model
4066+ int startModelIndex;
4067+
4068+ // Ending index of the change, in terms of the model
4069+ int endModelIndex;
4070+
4071+ // Type of change
4072+ int type;
4073+
4074+ // Number of rows in the model
4075+ int modelRowCount;
4076+
4077+ // The event that triggered this.
4078+ TableModelEvent event;
4079+
4080+ // Length of the change (end - start + 1)
4081+ int length;
4082+
4083+ // True if the event indicates all the contents have changed
4084+ boolean allRowsChanged;
4085+
4086+ ModelChange(TableModelEvent e) {
4087+ startModelIndex = Math.max(0, e.getFirstRow());
4088+ endModelIndex = e.getLastRow();
4089+ modelRowCount = getModel().getRowCount();
4090+ if (endModelIndex < 0) {
4091+ endModelIndex = Math.max(0, modelRowCount - 1);
4092+ }
4093+ length = endModelIndex - startModelIndex + 1;
4094+ type = e.getType();
4095+ event = e;
4096+ allRowsChanged = (e.getLastRow() == Integer.MAX_VALUE);
4097+ }
4098+ }
4099+
4100+ /**
4101+ * Invoked when <code>sorterChanged</code> is invoked, or
4102+ * when <code>tableChanged</code> is invoked and sorting is enabled.
4103+ */
4104+ private void sortedTableChanged(RowSorterEvent sortedEvent,
4105+ TableModelEvent e) {
4106+ int editingModelIndex = -1;
4107+ ModelChange change = (e != null) ? new ModelChange(e) : null;
4108+
4109+ if ((change == null || !change.allRowsChanged) &&
4110+ this.editingRow != -1) {
4111+ editingModelIndex = convertRowIndexToModel(sortedEvent,
4112+ this.editingRow);
4113+ }
4114+
4115+ sortManager.prepareForChange(sortedEvent, change);
4116+
4117+ if (e != null) {
4118+ if (change.type == TableModelEvent.UPDATE) {
4119+ repaintSortedRows(change);
4120+ }
4121+ notifySorter(change);
4122+ if (change.type != TableModelEvent.UPDATE) {
4123+ // If the Sorter is unsorted we will not have received
4124+ // notification, force treating insert/delete as a change.
4125+ sorterChanged = true;
4126+ }
4127+ }
4128+ else {
4129+ sorterChanged = true;
4130+ }
4131+
4132+ sortManager.processChange(sortedEvent, change, sorterChanged);
4133+
4134+ if (sorterChanged) {
4135+ // Update the editing row
4136+ if (this.editingRow != -1) {
4137+ int newIndex = (editingModelIndex == -1) ? -1 :
4138+ convertRowIndexToView(editingModelIndex,change);
4139+ restoreSortingEditingRow(newIndex);
4140+ }
4141+
4142+ // And handle the appropriate repainting.
4143+ if (e == null || change.type != TableModelEvent.UPDATE) {
4144+ resizeAndRepaint();
4145+ }
4146+ }
4147+
4148+ // Check if lead/anchor need to be reset.
4149+ if (change != null && change.allRowsChanged) {
4150+ clearSelectionAndLeadAnchor();
4151+ resizeAndRepaint();
4152+ }
4153+ }
4154+
4155+ /**
4156+ * Repaints the sort of sorted rows in response to a TableModelEvent.
4157+ */
4158+ private void repaintSortedRows(ModelChange change) {
4159+ if (change.startModelIndex > change.endModelIndex ||
4160+ change.startModelIndex + 10 < change.endModelIndex) {
4161+ // Too much has changed, punt
4162+ repaint();
4163+ return;
4164+ }
4165+ int eventColumn = change.event.getColumn();
4166+ int columnViewIndex = eventColumn;
4167+ if (columnViewIndex == TableModelEvent.ALL_COLUMNS) {
4168+ columnViewIndex = 0;
4169+ }
4170+ else {
4171+ columnViewIndex = convertColumnIndexToView(columnViewIndex);
4172+ if (columnViewIndex == -1) {
4173+ return;
4174+ }
4175+ }
4176+ int modelIndex = change.startModelIndex;
4177+ while (modelIndex <= change.endModelIndex) {
4178+ int viewIndex = convertRowIndexToView(modelIndex++);
4179+ if (viewIndex != -1) {
4180+ Rectangle dirty = getCellRect(viewIndex, columnViewIndex,
4181+ false);
4182+ int x = dirty.x;
4183+ int w = dirty.width;
4184+ if (eventColumn == TableModelEvent.ALL_COLUMNS) {
4185+ x = 0;
4186+ w = getWidth();
4187+ }
4188+ repaint(x, dirty.y, w, dirty.height);
4189+ }
4190+ }
4191+ }
4192+
4193+ /**
4194+ * Restores the selection after a model event/sort order changes.
4195+ * All coordinates are in terms of the model.
4196+ */
4197+ private void restoreSortingSelection(int[] selection, int lead,
4198+ ModelChange change) {
4199+ // Convert the selection from model to view
4200+ for (int i = selection.length - 1; i >= 0; i--) {
4201+ selection[i] = convertRowIndexToView(selection[i], change);
4202+ }
4203+ lead = convertRowIndexToView(lead, change);
4204+
4205+ // Check for the common case of no change in selection for 1 row
4206+ if (selection.length == 0 ||
4207+ (selection.length == 1 && selection[0] == getSelectedRow())) {
4208+ return;
4209+ }
4210+
4211+ // And apply the new selection
4212+ selectionModel.setValueIsAdjusting(true);
4213+ selectionModel.clearSelection();
4214+ for (int i = selection.length - 1; i >= 0; i--) {
4215+ if (selection[i] != -1) {
4216+ selectionModel.addSelectionInterval(selection[i],
4217+ selection[i]);
4218+ }
4219+ }
4220+ SwingUtilities2.setLeadAnchorWithoutSelection(
4221+ selectionModel, lead, lead);
4222+ selectionModel.setValueIsAdjusting(false);
4223+ }
4224+
4225+ /**
4226+ * Restores the editing row after a model event/sort order change.
4227+ *
4228+ * @param editingRow new index of the editingRow, in terms of the view
4229+ */
4230+ private void restoreSortingEditingRow(int editingRow) {
4231+ if (editingRow == -1) {
4232+ // Editing row no longer being shown, cancel editing
4233+ TableCellEditor editor = getCellEditor();
4234+ if (editor != null) {
4235+ // First try and cancel
4236+ editor.cancelCellEditing();
4237+ if (getCellEditor() != null) {
4238+ // CellEditor didn't cede control, forcefully
4239+ // remove it
4240+ removeEditor();
4241+ }
4242+ }
4243+ }
4244+ else {
4245+ // Repositioning handled in BasicTableUI
4246+ this.editingRow = editingRow;
4247+ repaint();
4248+ }
4249+ }
4250+
4251+ /**
4252+ * Notifies the sorter of a change in the underlying model.
4253+ */
4254+ private void notifySorter(ModelChange change) {
4255+ try {
4256+ ignoreSortChange = true;
4257+ sorterChanged = false;
4258+ switch(change.type) {
4259+ case TableModelEvent.UPDATE:
4260+ if (change.event.getLastRow() == Integer.MAX_VALUE) {
4261+ sortManager.sorter.allRowsChanged();
4262+ } else if (change.event.getColumn() ==
4263+ TableModelEvent.ALL_COLUMNS) {
4264+ sortManager.sorter.rowsUpdated(change.startModelIndex,
4265+ change.endModelIndex);
4266+ } else {
4267+ sortManager.sorter.rowsUpdated(change.startModelIndex,
4268+ change.endModelIndex,
4269+ change.event.getColumn());
4270+ }
4271+ break;
4272+ case TableModelEvent.INSERT:
4273+ sortManager.sorter.rowsInserted(change.startModelIndex,
4274+ change.endModelIndex);
4275+ break;
4276+ case TableModelEvent.DELETE:
4277+ sortManager.sorter.rowsDeleted(change.startModelIndex,
4278+ change.endModelIndex);
4279+ break;
4280+ }
4281+ } finally {
4282+ ignoreSortChange = false;
4283+ }
4284+ }
4285+
4286+ /**
4287+ * Converts a model index to view index. This is called when the
4288+ * sorter or model changes and sorting is enabled.
4289+ *
4290+ * @param change describes the TableModelEvent that initiated the change;
4291+ * will be null if called as the result of a sort
4292+ */
4293+ private int convertRowIndexToView(int modelIndex, ModelChange change) {
4294+ if (modelIndex < 0) {
4295+ return -1;
4296+ }
4297+ if (change != null && modelIndex >= change.startModelIndex) {
4298+ if (change.type == TableModelEvent.INSERT) {
4299+ if (modelIndex + change.length >= change.modelRowCount) {
4300+ return -1;
4301+ }
4302+ return sortManager.sorter.convertRowIndexToView(
4303+ modelIndex + change.length);
4304+ }
4305+ else if (change.type == TableModelEvent.DELETE) {
4306+ if (modelIndex <= change.endModelIndex) {
4307+ // deleted
4308+ return -1;
4309+ }
4310+ else {
4311+ if (modelIndex - change.length >= change.modelRowCount) {
4312+ return -1;
4313+ }
4314+ return sortManager.sorter.convertRowIndexToView(
4315+ modelIndex - change.length);
4316+ }
4317+ }
4318+ // else, updated
4319+ }
4320+ if (modelIndex >= getModel().getRowCount()) {
4321+ return -1;
4322+ }
4323+ return sortManager.sorter.convertRowIndexToView(modelIndex);
4324+ }
4325+
4326+ /**
4327+ * Converts the selection to model coordinates. This is used when
4328+ * the model changes or the sorter changes.
4329+ */
4330+ private int[] convertSelectionToModel(RowSorterEvent e) {
4331+ int[] selection = getSelectedRows();
4332+ for (int i = selection.length - 1; i >= 0; i--) {
4333+ selection[i] = convertRowIndexToModel(e, selection[i]);
4334+ }
4335+ return selection;
4336+ }
4337+
4338+ private int convertRowIndexToModel(RowSorterEvent e, int viewIndex) {
4339+ if (e != null) {
4340+ if (e.getPreviousRowCount() == 0) {
4341+ return viewIndex;
4342+ }
4343+ // range checking handled by RowSorterEvent
4344+ return e.convertPreviousRowIndexToModel(viewIndex);
4345+ }
4346+ // Make sure the viewIndex is valid
4347+ if (viewIndex < 0 || viewIndex >= getRowCount()) {
4348+ return -1;
4349+ }
4350+ return convertRowIndexToModel(viewIndex);
4351+ }
4352+
4353+//
4354+// Implementing TableModelListener interface
4355+//
4356+
4357+ /**
4358+ * Invoked when this table's <code>TableModel</code> generates
4359+ * a <code>TableModelEvent</code>.
4360+ * The <code>TableModelEvent</code> should be constructed in the
4361+ * coordinate system of the model; the appropriate mapping to the
4362+ * view coordinate system is performed by this <code>JTable</code>
4363+ * when it receives the event.
4364+ * <p>
4365+ * Application code will not use these methods explicitly, they
4366+ * are used internally by <code>JTable</code>.
4367+ * <p>
4368+ * Note that as of 1.3, this method clears the selection, if any.
4369+ */
4370+ public void tableChanged(TableModelEvent e) {
4371+ if (e == null || e.getFirstRow() == TableModelEvent.HEADER_ROW) {
4372+ // The whole thing changed
4373+ clearSelectionAndLeadAnchor();
4374+
4375+ rowModel = null;
4376+
4377+ if (sortManager != null) {
4378+ try {
4379+ ignoreSortChange = true;
4380+ sortManager.sorter.modelStructureChanged();
4381+ } finally {
4382+ ignoreSortChange = false;
4383+ }
4384+ sortManager.allChanged();
4385+ }
4386+
4387+ if (getAutoCreateColumnsFromModel()) {
4388+ // This will effect invalidation of the JTable and JTableHeader.
4389+ createDefaultColumnsFromModel();
4390+ return;
4391+ }
4392+
4393+ resizeAndRepaint();
4394+ return;
4395+ }
4396+
4397+ if (sortManager != null) {
4398+ sortedTableChanged(null, e);
4399+ return;
4400+ }
4401+
4402+ // The totalRowHeight calculated below will be incorrect if
4403+ // there are variable height rows. Repaint the visible region,
4404+ // but don't return as a revalidate may be necessary as well.
4405+ if (rowModel != null) {
4406+ repaint();
4407+ }
4408+
4409+ if (e.getType() == TableModelEvent.INSERT) {
4410+ tableRowsInserted(e);
4411+ return;
4412+ }
4413+
4414+ if (e.getType() == TableModelEvent.DELETE) {
4415+ tableRowsDeleted(e);
4416+ return;
4417+ }
4418+
4419+ int modelColumn = e.getColumn();
4420+ int start = e.getFirstRow();
4421+ int end = e.getLastRow();
4422+
4423+ Rectangle dirtyRegion;
4424+ if (modelColumn == TableModelEvent.ALL_COLUMNS) {
4425+ // 1 or more rows changed
4426+ dirtyRegion = new Rectangle(0, start * getRowHeight(),
4427+ getColumnModel().getTotalColumnWidth(), 0);
4428+ }
4429+ else {
4430+ // A cell or column of cells has changed.
4431+ // Unlike the rest of the methods in the JTable, the TableModelEvent
4432+ // uses the coordinate system of the model instead of the view.
4433+ // This is the only place in the JTable where this "reverse mapping"
4434+ // is used.
4435+ int column = convertColumnIndexToView(modelColumn);
4436+ dirtyRegion = getCellRect(start, column, false);
4437+ }
4438+
4439+ // Now adjust the height of the dirty region according to the value of "end".
4440+ // Check for Integer.MAX_VALUE as this will cause an overflow.
4441+ if (end != Integer.MAX_VALUE) {
4442+ dirtyRegion.height = (end-start+1)*getRowHeight();
4443+ repaint(dirtyRegion.x, dirtyRegion.y, dirtyRegion.width, dirtyRegion.height);
4444+ }
4445+ // In fact, if the end is Integer.MAX_VALUE we need to revalidate anyway
4446+ // because the scrollbar may need repainting.
4447+ else {
4448+ clearSelectionAndLeadAnchor();
4449+ resizeAndRepaint();
4450+ rowModel = null;
4451+ }
4452+ }
4453+
4454+ /*
4455+ * Invoked when rows have been inserted into the table.
4456+ * <p>
4457+ * Application code will not use these methods explicitly, they
4458+ * are used internally by JTable.
4459+ *
4460+ * @param e the TableModelEvent encapsulating the insertion
4461+ */
4462+ private void tableRowsInserted(TableModelEvent e) {
4463+ int start = e.getFirstRow();
4464+ int end = e.getLastRow();
4465+ if (start < 0) {
4466+ start = 0;
4467+ }
4468+ if (end < 0) {
4469+ end = getRowCount()-1;
4470+ }
4471+
4472+ // Adjust the selection to account for the new rows.
4473+ int length = end - start + 1;
4474+ selectionModel.insertIndexInterval(start, length, true);
4475+
4476+ // If we have variable height rows, adjust the row model.
4477+ if (rowModel != null) {
4478+ rowModel.insertEntries(start, length, getRowHeight());
4479+ }
4480+ int rh = getRowHeight() ;
4481+ Rectangle drawRect = new Rectangle(0, start * rh,
4482+ getColumnModel().getTotalColumnWidth(),
4483+ (getRowCount()-start) * rh);
4484+
4485+ revalidate();
4486+ // PENDING(milne) revalidate calls repaint() if parent is a ScrollPane
4487+ // repaint still required in the unusual case where there is no ScrollPane
4488+ repaint(drawRect);
4489+ }
4490+
4491+ /*
4492+ * Invoked when rows have been removed from the table.
4493+ * <p>
4494+ * Application code will not use these methods explicitly, they
4495+ * are used internally by JTable.
4496+ *
4497+ * @param e the TableModelEvent encapsulating the deletion
4498+ */
4499+ private void tableRowsDeleted(TableModelEvent e) {
4500+ int start = e.getFirstRow();
4501+ int end = e.getLastRow();
4502+ if (start < 0) {
4503+ start = 0;
4504+ }
4505+ if (end < 0) {
4506+ end = getRowCount()-1;
4507+ }
4508+
4509+ int deletedCount = end - start + 1;
4510+ int previousRowCount = getRowCount() + deletedCount;
4511+ // Adjust the selection to account for the new rows
4512+ selectionModel.removeIndexInterval(start, end);
4513+
4514+ // If we have variable height rows, adjust the row model.
4515+ if (rowModel != null) {
4516+ rowModel.removeEntries(start, deletedCount);
4517+ }
4518+
4519+ int rh = getRowHeight();
4520+ Rectangle drawRect = new Rectangle(0, start * rh,
4521+ getColumnModel().getTotalColumnWidth(),
4522+ (previousRowCount - start) * rh);
4523+
4524+ revalidate();
4525+ // PENDING(milne) revalidate calls repaint() if parent is a ScrollPane
4526+ // repaint still required in the unusual case where there is no ScrollPane
4527+ repaint(drawRect);
4528+ }
4529+
4530+//
4531+// Implementing TableColumnModelListener interface
4532+//
4533+
4534+ /**
4535+ * Invoked when a column is added to the table column model.
4536+ * <p>
4537+ * Application code will not use these methods explicitly, they
4538+ * are used internally by JTable.
4539+ *
4540+ * @see TableColumnModelListener
4541+ */
4542+ public void columnAdded(TableColumnModelEvent e) {
4543+ // If I'm currently editing, then I should stop editing
4544+ if (isEditing()) {
4545+ removeEditor();
4546+ }
4547+ resizeAndRepaint();
4548+ }
4549+
4550+ /**
4551+ * Invoked when a column is removed from the table column model.
4552+ * <p>
4553+ * Application code will not use these methods explicitly, they
4554+ * are used internally by JTable.
4555+ *
4556+ * @see TableColumnModelListener
4557+ */
4558+ public void columnRemoved(TableColumnModelEvent e) {
4559+ // If I'm currently editing, then I should stop editing
4560+ if (isEditing()) {
4561+ removeEditor();
4562+ }
4563+ resizeAndRepaint();
4564+ }
4565+
4566+ /**
4567+ * Invoked when a column is repositioned. If a cell is being
4568+ * edited, then editing is stopped and the cell is redrawn.
4569+ * <p>
4570+ * Application code will not use these methods explicitly, they
4571+ * are used internally by JTable.
4572+ *
4573+ * @param e the event received
4574+ * @see TableColumnModelListener
4575+ */
4576+ public void columnMoved(TableColumnModelEvent e) {
4577+ if (isEditing() && !getCellEditor().stopCellEditing()) {
4578+ getCellEditor().cancelCellEditing();
4579+ }
4580+ repaint();
4581+ }
4582+
4583+ /**
4584+ * Invoked when a column is moved due to a margin change.
4585+ * If a cell is being edited, then editing is stopped and the cell
4586+ * is redrawn.
4587+ * <p>
4588+ * Application code will not use these methods explicitly, they
4589+ * are used internally by JTable.
4590+ *
4591+ * @param e the event received
4592+ * @see TableColumnModelListener
4593+ */
4594+ public void columnMarginChanged(ChangeEvent e) {
4595+ if (isEditing() && !getCellEditor().stopCellEditing()) {
4596+ getCellEditor().cancelCellEditing();
4597+ }
4598+ TableColumn resizingColumn = getResizingColumn();
4599+ // Need to do this here, before the parent's
4600+ // layout manager calls getPreferredSize().
4601+ if (resizingColumn != null && autoResizeMode == AUTO_RESIZE_OFF) {
4602+ resizingColumn.setPreferredWidth(resizingColumn.getWidth());
4603+ }
4604+ resizeAndRepaint();
4605+ }
4606+
4607+ private int limit(int i, int a, int b) {
4608+ return Math.min(b, Math.max(i, a));
4609+ }
4610+
4611+ /**
4612+ * Invoked when the selection model of the <code>TableColumnModel</code>
4613+ * is changed.
4614+ * <p>
4615+ * Application code will not use these methods explicitly, they
4616+ * are used internally by JTable.
4617+ *
4618+ * @param e the event received
4619+ * @see TableColumnModelListener
4620+ */
4621+ public void columnSelectionChanged(ListSelectionEvent e) {
4622+ boolean isAdjusting = e.getValueIsAdjusting();
4623+ if (columnSelectionAdjusting && !isAdjusting) {
4624+ // The assumption is that when the model is no longer adjusting
4625+ // we will have already gotten all the changes, and therefore
4626+ // don't need to do an additional paint.
4627+ columnSelectionAdjusting = false;
4628+ return;
4629+ }
4630+ columnSelectionAdjusting = isAdjusting;
4631+ // The getCellRect() call will fail unless there is at least one row.
4632+ if (getRowCount() <= 0 || getColumnCount() <= 0) {
4633+ return;
4634+ }
4635+ int firstIndex = limit(e.getFirstIndex(), 0, getColumnCount()-1);
4636+ int lastIndex = limit(e.getLastIndex(), 0, getColumnCount()-1);
4637+ int minRow = 0;
4638+ int maxRow = getRowCount() - 1;
4639+ if (getRowSelectionAllowed()) {
4640+ minRow = selectionModel.getMinSelectionIndex();
4641+ maxRow = selectionModel.getMaxSelectionIndex();
4642+ int leadRow = getAdjustedIndex(selectionModel.getLeadSelectionIndex(), true);
4643+
4644+ if (minRow == -1 || maxRow == -1) {
4645+ if (leadRow == -1) {
4646+ // nothing to repaint, return
4647+ return;
4648+ }
4649+
4650+ // only thing to repaint is the lead
4651+ minRow = maxRow = leadRow;
4652+ } else {
4653+ // We need to consider more than just the range between
4654+ // the min and max selected index. The lead row, which could
4655+ // be outside this range, should be considered also.
4656+ if (leadRow != -1) {
4657+ minRow = Math.min(minRow, leadRow);
4658+ maxRow = Math.max(maxRow, leadRow);
4659+ }
4660+ }
4661+ }
4662+ Rectangle firstColumnRect = getCellRect(minRow, firstIndex, false);
4663+ Rectangle lastColumnRect = getCellRect(maxRow, lastIndex, false);
4664+ Rectangle dirtyRegion = firstColumnRect.union(lastColumnRect);
4665+ repaint(dirtyRegion);
4666+ }
4667+
4668+//
4669+// Implementing ListSelectionListener interface
4670+//
4671+
4672+ /**
4673+ * Invoked when the row selection changes -- repaints to show the new
4674+ * selection.
4675+ * <p>
4676+ * Application code will not use these methods explicitly, they
4677+ * are used internally by JTable.
4678+ *
4679+ * @param e the event received
4680+ * @see ListSelectionListener
4681+ */
4682+ public void valueChanged(ListSelectionEvent e) {
4683+ if (sortManager != null) {
4684+ sortManager.viewSelectionChanged(e);
4685+ }
4686+ boolean isAdjusting = e.getValueIsAdjusting();
4687+ if (rowSelectionAdjusting && !isAdjusting) {
4688+ // The assumption is that when the model is no longer adjusting
4689+ // we will have already gotten all the changes, and therefore
4690+ // don't need to do an additional paint.
4691+ rowSelectionAdjusting = false;
4692+ return;
4693+ }
4694+ rowSelectionAdjusting = isAdjusting;
4695+ // The getCellRect() calls will fail unless there is at least one column.
4696+ if (getRowCount() <= 0 || getColumnCount() <= 0) {
4697+ return;
4698+ }
4699+ int firstIndex = limit(e.getFirstIndex(), 0, getRowCount()-1);
4700+ int lastIndex = limit(e.getLastIndex(), 0, getRowCount()-1);
4701+ Rectangle firstRowRect = getCellRect(firstIndex, 0, false);
4702+ Rectangle lastRowRect = getCellRect(lastIndex, getColumnCount()-1, false);
4703+ Rectangle dirtyRegion = firstRowRect.union(lastRowRect);
4704+ repaint(dirtyRegion);
4705+ }
4706+
4707+//
4708+// Implementing the CellEditorListener interface
4709+//
4710+
4711+ /**
4712+ * Invoked when editing is finished. The changes are saved and the
4713+ * editor is discarded.
4714+ * <p>
4715+ * Application code will not use these methods explicitly, they
4716+ * are used internally by JTable.
4717+ *
4718+ * @param e the event received
4719+ * @see CellEditorListener
4720+ */
4721+ public void editingStopped(ChangeEvent e) {
4722+ // Take in the new value
4723+ TableCellEditor editor = getCellEditor();
4724+ if (editor != null) {
4725+ Object value = editor.getCellEditorValue();
4726+ setValueAt(value, editingRow, editingColumn);
4727+ removeEditor();
4728+ }
4729+ }
4730+
4731+ /**
4732+ * Invoked when editing is canceled. The editor object is discarded
4733+ * and the cell is rendered once again.
4734+ * <p>
4735+ * Application code will not use these methods explicitly, they
4736+ * are used internally by JTable.
4737+ *
4738+ * @param e the event received
4739+ * @see CellEditorListener
4740+ */
4741+ public void editingCanceled(ChangeEvent e) {
4742+ removeEditor();
4743+ }
4744+
4745+//
4746+// Implementing the Scrollable interface
4747+//
4748+
4749+ /**
4750+ * Sets the preferred size of the viewport for this table.
4751+ *
4752+ * @param size a <code>Dimension</code> object specifying the <code>preferredSize</code> of a
4753+ * <code>JViewport</code> whose view is this table
4754+ * @see Scrollable#getPreferredScrollableViewportSize
4755+ * @beaninfo
4756+ * description: The preferred size of the viewport.
4757+ */
4758+ public void setPreferredScrollableViewportSize(Dimension size) {
4759+ preferredViewportSize = size;
4760+ }
4761+
4762+ /**
4763+ * Returns the preferred size of the viewport for this table.
4764+ *
4765+ * @return a <code>Dimension</code> object containing the <code>preferredSize</code> of the <code>JViewport</code>
4766+ * which displays this table
4767+ * @see Scrollable#getPreferredScrollableViewportSize
4768+ */
4769+ public Dimension getPreferredScrollableViewportSize() {
4770+ return preferredViewportSize;
4771+ }
4772+
4773+ /**
4774+ * Returns the scroll increment (in pixels) that completely exposes one new
4775+ * row or column (depending on the orientation).
4776+ * <p>
4777+ * This method is called each time the user requests a unit scroll.
4778+ *
4779+ * @param visibleRect the view area visible within the viewport
4780+ * @param orientation either <code>SwingConstants.VERTICAL</code>
4781+ * or <code>SwingConstants.HORIZONTAL</code>
4782+ * @param direction less than zero to scroll up/left,
4783+ * greater than zero for down/right
4784+ * @return the "unit" increment for scrolling in the specified direction
4785+ * @see Scrollable#getScrollableUnitIncrement
4786+ */
4787+ public int getScrollableUnitIncrement(Rectangle visibleRect,
4788+ int orientation,
4789+ int direction) {
4790+ int leadingRow;
4791+ int leadingCol;
4792+ Rectangle leadingCellRect;
4793+
4794+ int leadingVisibleEdge;
4795+ int leadingCellEdge;
4796+ int leadingCellSize;
4797+
4798+ leadingRow = getLeadingRow(visibleRect);
4799+ leadingCol = getLeadingCol(visibleRect);
4800+ if (orientation == SwingConstants.VERTICAL && leadingRow < 0) {
4801+ // Couldn't find leading row - return some default value
4802+ return getRowHeight();
4803+ }
4804+ else if (orientation == SwingConstants.HORIZONTAL && leadingCol < 0) {
4805+ // Couldn't find leading col - return some default value
4806+ return 100;
4807+ }
4808+
4809+ // Note that it's possible for one of leadingCol or leadingRow to be
4810+ // -1, depending on the orientation. This is okay, as getCellRect()
4811+ // still provides enough information to calculate the unit increment.
4812+ leadingCellRect = getCellRect(leadingRow, leadingCol, true);
4813+ leadingVisibleEdge = leadingEdge(visibleRect, orientation);
4814+ leadingCellEdge = leadingEdge(leadingCellRect, orientation);
4815+
4816+ if (orientation == SwingConstants.VERTICAL) {
4817+ leadingCellSize = leadingCellRect.height;
4818+
4819+ }
4820+ else {
4821+ leadingCellSize = leadingCellRect.width;
4822+ }
4823+
4824+ // 4 cases:
4825+ // #1: Leading cell fully visible, reveal next cell
4826+ // #2: Leading cell fully visible, hide leading cell
4827+ // #3: Leading cell partially visible, hide rest of leading cell
4828+ // #4: Leading cell partially visible, reveal rest of leading cell
4829+
4830+ if (leadingVisibleEdge == leadingCellEdge) { // Leading cell is fully
4831+ // visible
4832+ // Case #1: Reveal previous cell
4833+ if (direction < 0) {
4834+ int retVal = 0;
4835+
4836+ if (orientation == SwingConstants.VERTICAL) {
4837+ // Loop past any zero-height rows
4838+ while (--leadingRow >= 0) {
4839+ retVal = getRowHeight(leadingRow);
4840+ if (retVal != 0) {
4841+ break;
4842+ }
4843+ }
4844+ }
4845+ else { // HORIZONTAL
4846+ // Loop past any zero-width cols
4847+ while (--leadingCol >= 0) {
4848+ retVal = getCellRect(leadingRow, leadingCol, true).width;
4849+ if (retVal != 0) {
4850+ break;
4851+ }
4852+ }
4853+ }
4854+ return retVal;
4855+ }
4856+ else { // Case #2: hide leading cell
4857+ return leadingCellSize;
4858+ }
4859+ }
4860+ else { // Leading cell is partially hidden
4861+ // Compute visible, hidden portions
4862+ int hiddenAmt = Math.abs(leadingVisibleEdge - leadingCellEdge);
4863+ int visibleAmt = leadingCellSize - hiddenAmt;
4864+
4865+ if (direction > 0) {
4866+ // Case #3: hide showing portion of leading cell
4867+ return visibleAmt;
4868+ }
4869+ else { // Case #4: reveal hidden portion of leading cell
4870+ return hiddenAmt;
4871+ }
4872+ }
4873+ }
4874+
4875+ /**
4876+ * Returns <code>visibleRect.height</code> or
4877+ * <code>visibleRect.width</code>,
4878+ * depending on this table's orientation. Note that as of Swing 1.1.1
4879+ * (Java 2 v 1.2.2) the value
4880+ * returned will ensure that the viewport is cleanly aligned on
4881+ * a row boundary.
4882+ *
4883+ * @return <code>visibleRect.height</code> or
4884+ * <code>visibleRect.width</code>
4885+ * per the orientation
4886+ * @see Scrollable#getScrollableBlockIncrement
4887+ */
4888+ public int getScrollableBlockIncrement(Rectangle visibleRect,
4889+ int orientation, int direction) {
4890+
4891+ if (getRowCount() == 0) {
4892+ // Short-circuit empty table model
4893+ if (SwingConstants.VERTICAL == orientation) {
4894+ int rh = getRowHeight();
4895+ return (rh > 0) ? Math.max(rh, (visibleRect.height / rh) * rh) :
4896+ visibleRect.height;
4897+ }
4898+ else {
4899+ return visibleRect.width;
4900+ }
4901+ }
4902+ // Shortcut for vertical scrolling of a table w/ uniform row height
4903+ if (null == rowModel && SwingConstants.VERTICAL == orientation) {
4904+ int row = rowAtPoint(visibleRect.getLocation());
4905+ assert row != -1;
4906+ int col = columnAtPoint(visibleRect.getLocation());
4907+ Rectangle cellRect = getCellRect(row, col, true);
4908+
4909+ if (cellRect.y == visibleRect.y) {
4910+ int rh = getRowHeight();
4911+ assert rh > 0;
4912+ return Math.max(rh, (visibleRect.height / rh) * rh);
4913+ }
4914+ }
4915+ if (direction < 0) {
4916+ return getPreviousBlockIncrement(visibleRect, orientation);
4917+ }
4918+ else {
4919+ return getNextBlockIncrement(visibleRect, orientation);
4920+ }
4921+ }
4922+
4923+ /**
4924+ * Called to get the block increment for upward scrolling in cases of
4925+ * horizontal scrolling, or for vertical scrolling of a table with
4926+ * variable row heights.
4927+ */
4928+ private int getPreviousBlockIncrement(Rectangle visibleRect,
4929+ int orientation) {
4930+ // Measure back from visible leading edge
4931+ // If we hit the cell on its leading edge, it becomes the leading cell.
4932+ // Else, use following cell
4933+
4934+ int row;
4935+ int col;
4936+
4937+ int newEdge;
4938+ Point newCellLoc;
4939+
4940+ int visibleLeadingEdge = leadingEdge(visibleRect, orientation);
4941+ boolean leftToRight = getComponentOrientation().isLeftToRight();
4942+ int newLeadingEdge;
4943+
4944+ // Roughly determine the new leading edge by measuring back from the
4945+ // leading visible edge by the size of the visible rect, and find the
4946+ // cell there.
4947+ if (orientation == SwingConstants.VERTICAL) {
4948+ newEdge = visibleLeadingEdge - visibleRect.height;
4949+ int x = visibleRect.x + (leftToRight ? 0 : visibleRect.width);
4950+ newCellLoc = new Point(x, newEdge);
4951+ }
4952+ else if (leftToRight) {
4953+ newEdge = visibleLeadingEdge - visibleRect.width;
4954+ newCellLoc = new Point(newEdge, visibleRect.y);
4955+ }
4956+ else { // Horizontal, right-to-left
4957+ newEdge = visibleLeadingEdge + visibleRect.width;
4958+ newCellLoc = new Point(newEdge - 1, visibleRect.y);
4959+ }
4960+ row = rowAtPoint(newCellLoc);
4961+ col = columnAtPoint(newCellLoc);
4962+
4963+ // If we're measuring past the beginning of the table, we get an invalid
4964+ // cell. Just go to the beginning of the table in this case.
4965+ if (orientation == SwingConstants.VERTICAL & row < 0) {
4966+ newLeadingEdge = 0;
4967+ }
4968+ else if (orientation == SwingConstants.HORIZONTAL & col < 0) {
4969+ if (leftToRight) {
4970+ newLeadingEdge = 0;
4971+ }
4972+ else {
4973+ newLeadingEdge = getWidth();
4974+ }
4975+ }
4976+ else {
4977+ // Refine our measurement
4978+ Rectangle newCellRect = getCellRect(row, col, true);
4979+ int newCellLeadingEdge = leadingEdge(newCellRect, orientation);
4980+ int newCellTrailingEdge = trailingEdge(newCellRect, orientation);
4981+
4982+ // Usually, we hit in the middle of newCell, and want to scroll to
4983+ // the beginning of the cell after newCell. But there are a
4984+ // couple corner cases where we want to scroll to the beginning of
4985+ // newCell itself. These cases are:
4986+ // 1) newCell is so large that it ends at or extends into the
4987+ // visibleRect (newCell is the leading cell, or is adjacent to
4988+ // the leading cell)
4989+ // 2) newEdge happens to fall right on the beginning of a cell
4990+
4991+ // Case 1
4992+ if ((orientation == SwingConstants.VERTICAL || leftToRight) &&
4993+ (newCellTrailingEdge >= visibleLeadingEdge)) {
4994+ newLeadingEdge = newCellLeadingEdge;
4995+ }
4996+ else if (orientation == SwingConstants.HORIZONTAL &&
4997+ !leftToRight &&
4998+ newCellTrailingEdge <= visibleLeadingEdge) {
4999+ newLeadingEdge = newCellLeadingEdge;

4608 more lines aren’t shown here.