1 /*
2 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
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
61 /**
62 * The <code>JTable</code> is used to display and edit regular two-dimensional tables
63 * of cells.
64 * See <a href="http://java.sun.com/docs/books/tutorial/uiswing/components/table.html">How to Use Tables</a>
65 * in <em>The Java Tutorial</em>
66 * for task-oriented documentation and examples of using <code>JTable</code>.
67 *
68 * <p>
69 * The <code>JTable</code> has many
70 * facilities that make it possible to customize its rendering and editing
71 * but provides defaults for these features so that simple tables can be
72 * set up easily. For example, to set up a table with 10 rows and 10
73 * columns of numbers:
74 * <p>
75 * <pre>
76 * TableModel dataModel = new AbstractTableModel() {
77 * public int getColumnCount() { return 10; }
78 * public int getRowCount() { return 10;}
79 * public Object getValueAt(int row, int col) { return new Integer(row*col); }
80 * };
81 * JTable table = new JTable(dataModel);
82 * JScrollPane scrollpane = new JScrollPane(table);
83 * </pre>
84 * <p>
85 * {@code JTable}s are typically placed inside of a {@code JScrollPane}. By
86 * default, a {@code JTable} will adjust its width such that
87 * a horizontal scrollbar is unnecessary. To allow for a horizontal scrollbar,
88 * invoke {@link #setAutoResizeMode} with {@code AUTO_RESIZE_OFF}.
89 * Note that if you wish to use a <code>JTable</code> in a standalone
90 * view (outside of a <code>JScrollPane</code>) and want the header
91 * displayed, you can get it using {@link #getTableHeader} and
92 * display it separately.
93 * <p>
94 * To enable sorting and filtering of rows, use a
95 * {@code RowSorter}.
96 * You can set up a row sorter in either of two ways:
97 * <ul>
98 * <li>Directly set the {@code RowSorter}. For example:
99 * {@code table.setRowSorter(new TableRowSorter(model))}.
100 * <li>Set the {@code autoCreateRowSorter}
101 * property to {@code true}, so that the {@code JTable}
102 * creates a {@code RowSorter} for
103 * you. For example: {@code setAutoCreateRowSorter(true)}.
104 * </ul>
105 * <p>
106 * When designing applications that use the <code>JTable</code> it is worth paying
107 * close attention to the data structures that will represent the table's data.
108 * The <code>DefaultTableModel</code> is a model implementation that
109 * uses a <code>Vector</code> of <code>Vector</code>s of <code>Object</code>s to
110 * store the cell values. As well as copying the data from an
111 * application into the <code>DefaultTableModel</code>,
112 * it is also possible to wrap the data in the methods of the
113 * <code>TableModel</code> interface so that the data can be passed to the
114 * <code>JTable</code> directly, as in the example above. This often results
115 * in more efficient applications because the model is free to choose the
116 * internal representation that best suits the data.
117 * A good rule of thumb for deciding whether to use the <code>AbstractTableModel</code>
118 * or the <code>DefaultTableModel</code> is to use the <code>AbstractTableModel</code>
119 * as the base class for creating subclasses and the <code>DefaultTableModel</code>
120 * when subclassing is not required.
121 * <p>
122 * The "TableExample" directory in the demo area of the source distribution
123 * gives a number of complete examples of <code>JTable</code> usage,
124 * covering how the <code>JTable</code> can be used to provide an
125 * editable view of data taken from a database and how to modify
126 * the columns in the display to use specialized renderers and editors.
127 * <p>
128 * The <code>JTable</code> uses integers exclusively to refer to both the rows and the columns
129 * of the model that it displays. The <code>JTable</code> simply takes a tabular range of cells
130 * and uses <code>getValueAt(int, int)</code> to retrieve the
131 * values from the model during painting. It is important to remember that
132 * the column and row indexes returned by various <code>JTable</code> methods
133 * are in terms of the <code>JTable</code> (the view) and are not
134 * necessarily the same indexes used by the model.
135 * <p>
136 * By default, columns may be rearranged in the <code>JTable</code> so that the
137 * view's columns appear in a different order to the columns in the model.
138 * This does not affect the implementation of the model at all: when the
139 * columns are reordered, the <code>JTable</code> maintains the new order of the columns
140 * internally and converts its column indices before querying the model.
141 * <p>
142 * So, when writing a <code>TableModel</code>, it is not necessary to listen for column
143 * reordering events as the model will be queried in its own coordinate
144 * system regardless of what is happening in the view.
145 * In the examples area there is a demonstration of a sorting algorithm making
146 * use of exactly this technique to interpose yet another coordinate system
147 * where the order of the rows is changed, rather than the order of the columns.
148 * <p>
149 * Similarly when using the sorting and filtering functionality
150 * provided by <code>RowSorter</code> the underlying
151 * <code>TableModel</code> does not need to know how to do sorting,
152 * rather <code>RowSorter</code> will handle it. Coordinate
153 * conversions will be necessary when using the row based methods of
154 * <code>JTable</code> with the underlying <code>TableModel</code>.
155 * All of <code>JTable</code>s row based methods are in terms of the
156 * <code>RowSorter</code>, which is not necessarily the same as that
157 * of the underlying <code>TableModel</code>. For example, the
158 * selection is always in terms of <code>JTable</code> so that when
159 * using <code>RowSorter</code> you will need to convert using
160 * <code>convertRowIndexToView</code> or
161 * <code>convertRowIndexToModel</code>. The following shows how to
162 * convert coordinates from <code>JTable</code> to that of the
163 * underlying model:
164 * <pre>
165 * int[] selection = table.getSelectedRows();
166 * for (int i = 0; i < selection.length; i++) {
167 * selection[i] = table.convertRowIndexToModel(selection[i]);
168 * }
169 * // selection is now in terms of the underlying TableModel
170 * </pre>
171 * <p>
172 * By default if sorting is enabled <code>JTable</code> will persist the
173 * selection and variable row heights in terms of the model on
174 * sorting. For example if row 0, in terms of the underlying model,
175 * is currently selected, after the sort row 0, in terms of the
176 * underlying model will be selected. Visually the selection may
177 * change, but in terms of the underlying model it will remain the
178 * same. The one exception to that is if the model index is no longer
179 * visible or was removed. For example, if row 0 in terms of model
180 * was filtered out the selection will be empty after the sort.
181 * <p>
182 * J2SE 5 adds methods to <code>JTable</code> to provide convenient access to some
183 * common printing needs. Simple new {@link #print()} methods allow for quick
184 * and easy addition of printing support to your application. In addition, a new
185 * {@link #getPrintable} method is available for more advanced printing needs.
186 * <p>
187 * As for all <code>JComponent</code> classes, you can use
188 * {@link InputMap} and {@link ActionMap} to associate an
189 * {@link Action} object with a {@link KeyStroke} and execute the
190 * action under specified conditions.
191 * <p>
192 * <strong>Warning:</strong> Swing is not thread safe. For more
193 * information see <a
194 * href="package-summary.html#threading">Swing's Threading
195 * Policy</a>.
196 * <p>
197 * <strong>Warning:</strong>
198 * Serialized objects of this class will not be compatible with
199 * future Swing releases. The current serialization support is
200 * appropriate for short term storage or RMI between applications running
201 * the same version of Swing. As of 1.4, support for long term storage
202 * of all JavaBeans<sup><font size="-2">TM</font></sup>
203 * has been added to the <code>java.beans</code> package.
204 * Please see {@link java.beans.XMLEncoder}.
205 *
206 *
207 * @beaninfo
208 * attribute: isContainer false
209 * description: A component which displays data in a two dimensional grid.
210 *
211 * @author Philip Milne
212 * @author Shannon Hickey (printing support)
213 * @see javax.swing.table.DefaultTableModel
214 * @see javax.swing.table.TableRowSorter
215 */
216 /* The first versions of the JTable, contained in Swing-0.1 through
217 * Swing-0.4, were written by Alan Chung.
218 */
219 public class JTable extends JComponent implements TableModelListener, Scrollable,
220 TableColumnModelListener, ListSelectionListener, CellEditorListener,
221 Accessible, RowSorterListener
222 {
223 //
224 // Static Constants
225 //
226
227 /**
228 * @see #getUIClassID
229 * @see #readObject
230 */
231 private static final String uiClassID = "TableUI";
232
233 /** Do not adjust column widths automatically; use a horizontal scrollbar instead. */
234 public static final int AUTO_RESIZE_OFF = 0;
235
236 /** When a column is adjusted in the UI, adjust the next column the opposite way. */
237 public static final int AUTO_RESIZE_NEXT_COLUMN = 1;
238
239 /** During UI adjustment, change subsequent columns to preserve the total width;
240 * this is the default behavior. */
241 public static final int AUTO_RESIZE_SUBSEQUENT_COLUMNS = 2;
242
243 /** During all resize operations, apply adjustments to the last column only. */
244 public static final int AUTO_RESIZE_LAST_COLUMN = 3;
245
246 /** During all resize operations, proportionately resize all columns. */
247 public static final int AUTO_RESIZE_ALL_COLUMNS = 4;
248
249
250 /**
251 * Printing modes, used in printing <code>JTable</code>s.
252 *
253 * @see #print(JTable.PrintMode, MessageFormat, MessageFormat,
254 * boolean, PrintRequestAttributeSet, boolean)
255 * @see #getPrintable
256 * @since 1.5
257 */
258 public enum PrintMode {
259
260 /**
261 * Printing mode that prints the table at its current size,
262 * spreading both columns and rows across multiple pages if necessary.
263 */
264 NORMAL,
265
266 /**
267 * Printing mode that scales the output smaller, if necessary,
268 * to fit the table's entire width (and thereby all columns) on each page;
269 * Rows are spread across multiple pages as necessary.
270 */
271 FIT_WIDTH
272 }
273
274
275 //
276 // Instance Variables
277 //
278
279 /** The <code>TableModel</code> of the table. */
280 protected TableModel dataModel;
281
282 /** The <code>TableColumnModel</code> of the table. */
283 protected TableColumnModel columnModel;
284
285 /** The <code>ListSelectionModel</code> of the table, used to keep track of row selections. */
286 protected ListSelectionModel selectionModel;
287
288 /** The <code>TableHeader</code> working with the table. */
289 protected JTableHeader tableHeader;
290
291 /** The height in pixels of each row in the table. */
292 protected int rowHeight;
293
294 /** The height in pixels of the margin between the cells in each row. */
295 protected int rowMargin;
296
297 /** The color of the grid. */
298 protected Color gridColor;
299
300 /** The table draws horizontal lines between cells if <code>showHorizontalLines</code> is true. */
301 protected boolean showHorizontalLines;
302
303 /** The table draws vertical lines between cells if <code>showVerticalLines</code> is true. */
304 protected boolean showVerticalLines;
305
306 /**
307 * Determines if the table automatically resizes the
308 * width of the table's columns to take up the entire width of the
309 * table, and how it does the resizing.
310 */
311 protected int autoResizeMode;
312
313 /**
314 * The table will query the <code>TableModel</code> to build the default
315 * set of columns if this is true.
316 */
317 protected boolean autoCreateColumnsFromModel;
318
319 /** Used by the <code>Scrollable</code> interface to determine the initial visible area. */
320 protected Dimension preferredViewportSize;
321
322 /** True if row selection is allowed in this table. */
323 protected boolean rowSelectionAllowed;
324
325 /**
326 * Obsolete as of Java 2 platform v1.3. Please use the
327 * <code>rowSelectionAllowed</code> property and the
328 * <code>columnSelectionAllowed</code> property of the
329 * <code>columnModel</code> instead. Or use the
330 * method <code>getCellSelectionEnabled</code>.
331 */
332 /*
333 * If true, both a row selection and a column selection
334 * can be non-empty at the same time, the selected cells are the
335 * the cells whose row and column are both selected.
336 */
337 protected boolean cellSelectionEnabled;
338
339 /** If editing, the <code>Component</code> that is handling the editing. */
340 transient protected Component editorComp;
341
342 /**
343 * The active cell editor object, that overwrites the screen real estate
344 * occupied by the current cell and allows the user to change its contents.
345 * {@code null} if the table isn't currently editing.
346 */
347 transient protected TableCellEditor cellEditor;
348
349 /** Identifies the column of the cell being edited. */
350 transient protected int editingColumn;
351
352 /** Identifies the row of the cell being edited. */
353 transient protected int editingRow;
354
355 /**
356 * A table of objects that display the contents of a cell,
357 * indexed by class as declared in <code>getColumnClass</code>
358 * in the <code>TableModel</code> interface.
359 */
360 transient protected Hashtable defaultRenderersByColumnClass;
361
362 /**
363 * A table of objects that display and edit the contents of a cell,
364 * indexed by class as declared in <code>getColumnClass</code>
365 * in the <code>TableModel</code> interface.
366 */
367 transient protected Hashtable defaultEditorsByColumnClass;
368
369 /** The foreground color of selected cells. */
370 protected Color selectionForeground;
371
372 /** The background color of selected cells. */
373 protected Color selectionBackground;
374
375 //
376 // Private state
377 //
378
379 // WARNING: If you directly access this field you should also change the
380 // SortManager.modelRowSizes field as well.
381 private SizeSequence rowModel;
382 private boolean dragEnabled;
383 private boolean surrendersFocusOnKeystroke;
384 private PropertyChangeListener editorRemover = null;
385 /**
386 * The last value of getValueIsAdjusting from the column selection models
387 * columnSelectionChanged notification. Used to test if a repaint is
388 * needed.
389 */
390 private boolean columnSelectionAdjusting;
391 /**
392 * The last value of getValueIsAdjusting from the row selection models
393 * valueChanged notification. Used to test if a repaint is needed.
394 */
395 private boolean rowSelectionAdjusting;
396
397 /**
398 * To communicate errors between threads during printing.
399 */
400 private Throwable printError;
401
402 /**
403 * True when setRowHeight(int) has been invoked.
404 */
405 private boolean isRowHeightSet;
406
407 /**
408 * If true, on a sort the selection is reset.
409 */
410 private boolean updateSelectionOnSort;
411
412 /**
413 * Information used in sorting.
414 */
415 private transient SortManager sortManager;
416
417 /**
418 * If true, when sorterChanged is invoked it's value is ignored.
419 */
420 private boolean ignoreSortChange;
421
422 /**
423 * Whether or not sorterChanged has been invoked.
424 */
425 private boolean sorterChanged;
426
427 /**
428 * If true, any time the model changes a new RowSorter is set.
429 */
430 private boolean autoCreateRowSorter;
431
432 /**
433 * Whether or not the table always fills the viewport height.
434 * @see #setFillsViewportHeight
435 * @see #getScrollableTracksViewportHeight
436 */
437 private boolean fillsViewportHeight;
438
439 /**
440 * The drop mode for this component.
441 */
442 private DropMode dropMode = DropMode.USE_SELECTION;
443
444 /**
445 * The drop location.
446 */
447 private transient DropLocation dropLocation;
448
449 /**
450 * A subclass of <code>TransferHandler.DropLocation</code> representing
451 * a drop location for a <code>JTable</code>.
452 *
453 * @see #getDropLocation
454 * @since 1.6
455 */
456 public static final class DropLocation extends TransferHandler.DropLocation {
457 private final int row;
458 private final int col;
459 private final boolean isInsertRow;
460 private final boolean isInsertCol;
461
462 private DropLocation(Point p, int row, int col,
463 boolean isInsertRow, boolean isInsertCol) {
464
465 super(p);
466 this.row = row;
467 this.col = col;
468 this.isInsertRow = isInsertRow;
469 this.isInsertCol = isInsertCol;
470 }
471
472 /**
473 * Returns the row index where a dropped item should be placed in the
474 * table. Interpretation of the value depends on the return of
475 * <code>isInsertRow()</code>. If that method returns
476 * <code>true</code> this value indicates the index where a new
477 * row should be inserted. Otherwise, it represents the value
478 * of an existing row on which the data was dropped. This index is
479 * in terms of the view.
480 * <p>
481 * <code>-1</code> indicates that the drop occurred over empty space,
482 * and no row could be calculated.
483 *
484 * @return the drop row
485 */
486 public int getRow() {
487 return row;
488 }
489
490 /**
491 * Returns the column index where a dropped item should be placed in the
492 * table. Interpretation of the value depends on the return of
493 * <code>isInsertColumn()</code>. If that method returns
494 * <code>true</code> this value indicates the index where a new
495 * column should be inserted. Otherwise, it represents the value
496 * of an existing column on which the data was dropped. This index is
497 * in terms of the view.
498 * <p>
499 * <code>-1</code> indicates that the drop occurred over empty space,
500 * and no column could be calculated.
501 *
502 * @return the drop row
503 */
504 public int getColumn() {
505 return col;
506 }
507
508 /**
509 * Returns whether or not this location represents an insert
510 * of a row.
511 *
512 * @return whether or not this is an insert row
513 */
514 public boolean isInsertRow() {
515 return isInsertRow;
516 }
517
518 /**
519 * Returns whether or not this location represents an insert
520 * of a column.
521 *
522 * @return whether or not this is an insert column
523 */
524 public boolean isInsertColumn() {
525 return isInsertCol;
526 }
527
528 /**
529 * Returns a string representation of this drop location.
530 * This method is intended to be used for debugging purposes,
531 * and the content and format of the returned string may vary
532 * between implementations.
533 *
534 * @return a string representation of this drop location
535 */
536 public String toString() {
537 return getClass().getName()
538 + "[dropPoint=" + getDropPoint() + ","
539 + "row=" + row + ","
540 + "column=" + col + ","
541 + "insertRow=" + isInsertRow + ","
542 + "insertColumn=" + isInsertCol + "]";
543 }
544 }
545
546 //
547 // Constructors
548 //
549
550 /**
551 * Constructs a default <code>JTable</code> that is initialized with a default
552 * data model, a default column model, and a default selection
553 * model.
554 *
555 * @see #createDefaultDataModel
556 * @see #createDefaultColumnModel
557 * @see #createDefaultSelectionModel
558 */
559 public JTable() {
560 this(null, null, null);
561 }
562
563 /**
564 * Constructs a <code>JTable</code> that is initialized with
565 * <code>dm</code> as the data model, a default column model,
566 * and a default selection model.
567 *
568 * @param dm the data model for the table
569 * @see #createDefaultColumnModel
570 * @see #createDefaultSelectionModel
571 */
572 public JTable(TableModel dm) {
573 this(dm, null, null);
574 }
575
576 /**
577 * Constructs a <code>JTable</code> that is initialized with
578 * <code>dm</code> as the data model, <code>cm</code>
579 * as the column model, and a default selection model.
580 *
581 * @param dm the data model for the table
582 * @param cm the column model for the table
583 * @see #createDefaultSelectionModel
584 */
585 public JTable(TableModel dm, TableColumnModel cm) {
586 this(dm, cm, null);
587 }
588
589 /**
590 * Constructs a <code>JTable</code> that is initialized with
591 * <code>dm</code> as the data model, <code>cm</code> as the
592 * column model, and <code>sm</code> as the selection model.
593 * If any of the parameters are <code>null</code> this method
594 * will initialize the table with the corresponding default model.
595 * The <code>autoCreateColumnsFromModel</code> flag is set to false
596 * if <code>cm</code> is non-null, otherwise it is set to true
597 * and the column model is populated with suitable
598 * <code>TableColumns</code> for the columns in <code>dm</code>.
599 *
600 * @param dm the data model for the table
601 * @param cm the column model for the table
602 * @param sm the row selection model for the table
603 * @see #createDefaultDataModel
604 * @see #createDefaultColumnModel
605 * @see #createDefaultSelectionModel
606 */
607 public JTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) {
608 super();
609 setLayout(null);
610
611 setFocusTraversalKeys(KeyboardFocusManager.FORWARD_TRAVERSAL_KEYS,
612 JComponent.getManagingFocusForwardTraversalKeys());
613 setFocusTraversalKeys(KeyboardFocusManager.BACKWARD_TRAVERSAL_KEYS,
614 JComponent.getManagingFocusBackwardTraversalKeys());
615 if (cm == null) {
616 cm = createDefaultColumnModel();
617 autoCreateColumnsFromModel = true;
618 }
619 setColumnModel(cm);
620
621 if (sm == null) {
622 sm = createDefaultSelectionModel();
623 }
624 setSelectionModel(sm);
625
626 // Set the model last, that way if the autoCreatColumnsFromModel has
627 // been set above, we will automatically populate an empty columnModel
628 // with suitable columns for the new model.
629 if (dm == null) {
630 dm = createDefaultDataModel();
631 }
632 setModel(dm);
633
634 initializeLocalVars();
635 updateUI();
636 }
637
638 /**
639 * Constructs a <code>JTable</code> with <code>numRows</code>
640 * and <code>numColumns</code> of empty cells using
641 * <code>DefaultTableModel</code>. The columns will have
642 * names of the form "A", "B", "C", etc.
643 *
644 * @param numRows the number of rows the table holds
645 * @param numColumns the number of columns the table holds
646 * @see javax.swing.table.DefaultTableModel
647 */
648 public JTable(int numRows, int numColumns) {
649 this(new DefaultTableModel(numRows, numColumns));
650 }
651
652 /**
653 * Constructs a <code>JTable</code> to display the values in the
654 * <code>Vector</code> of <code>Vectors</code>, <code>rowData</code>,
655 * with column names, <code>columnNames</code>. The
656 * <code>Vectors</code> contained in <code>rowData</code>
657 * should contain the values for that row. In other words,
658 * the value of the cell at row 1, column 5 can be obtained
659 * with the following code:
660 * <p>
661 * <pre>((Vector)rowData.elementAt(1)).elementAt(5);</pre>
662 * <p>
663 * @param rowData the data for the new table
664 * @param columnNames names of each column
665 */
666 public JTable(Vector rowData, Vector columnNames) {
667 this(new DefaultTableModel(rowData, columnNames));
668 }
669
670 /**
671 * Constructs a <code>JTable</code> to display the values in the two dimensional array,
672 * <code>rowData</code>, with column names, <code>columnNames</code>.
673 * <code>rowData</code> is an array of rows, so the value of the cell at row 1,
674 * column 5 can be obtained with the following code:
675 * <p>
676 * <pre> rowData[1][5]; </pre>
677 * <p>
678 * All rows must be of the same length as <code>columnNames</code>.
679 * <p>
680 * @param rowData the data for the new table
681 * @param columnNames names of each column
682 */
683 public JTable(final Object[][] rowData, final Object[] columnNames) {
684 this(new AbstractTableModel() {
685 public String getColumnName(int column) { return columnNames[column].toString(); }
686 public int getRowCount() { return rowData.length; }
687 public int getColumnCount() { return columnNames.length; }
688 public Object getValueAt(int row, int col) { return rowData[row][col]; }
689 public boolean isCellEditable(int row, int column) { return true; }
690 public void setValueAt(Object value, int row, int col) {
691 rowData[row][col] = value;
692 fireTableCellUpdated(row, col);
693 }
694 });
695 }
696
697 /**
698 * Calls the <code>configureEnclosingScrollPane</code> method.
699 *
700 * @see #configureEnclosingScrollPane
701 */
702 public void addNotify() {
703 super.addNotify();
704 configureEnclosingScrollPane();
705 }
706
707 /**
708 * If this <code>JTable</code> is the <code>viewportView</code> of an enclosing <code>JScrollPane</code>
709 * (the usual situation), configure this <code>ScrollPane</code> by, amongst other things,
710 * installing the table's <code>tableHeader</code> as the <code>columnHeaderView</code> of the scroll pane.
711 * When a <code>JTable</code> is added to a <code>JScrollPane</code> in the usual way,
712 * using <code>new JScrollPane(myTable)</code>, <code>addNotify</code> is
713 * called in the <code>JTable</code> (when the table is added to the viewport).
714 * <code>JTable</code>'s <code>addNotify</code> method in turn calls this method,
715 * which is protected so that this default installation procedure can
716 * be overridden by a subclass.
717 *
718 * @see #addNotify
719 */
720 protected void configureEnclosingScrollPane() {
721 Container p = getParent();
722 if (p instanceof JViewport) {
723 Container gp = p.getParent();
724 if (gp instanceof JScrollPane) {
725 JScrollPane scrollPane = (JScrollPane)gp;
726 // Make certain we are the viewPort's view and not, for
727 // example, the rowHeaderView of the scrollPane -
728 // an implementor of fixed columns might do this.
729 JViewport viewport = scrollPane.getViewport();
730 if (viewport == null || viewport.getView() != this) {
731 return;
732 }
733 scrollPane.setColumnHeaderView(getTableHeader());
734 // scrollPane.getViewport().setBackingStoreEnabled(true);
735 Border border = scrollPane.getBorder();
736 if (border == null || border instanceof UIResource) {
737 Border scrollPaneBorder =
738 UIManager.getBorder("Table.scrollPaneBorder");
739 if (scrollPaneBorder != null) {
740 scrollPane.setBorder(scrollPaneBorder);
741 }
742 }
743 }
744 }
745 }
746
747 /**
748 * Calls the <code>unconfigureEnclosingScrollPane</code> method.
749 *
750 * @see #unconfigureEnclosingScrollPane
751 */
752 public void removeNotify() {
753 KeyboardFocusManager.getCurrentKeyboardFocusManager().
754 removePropertyChangeListener("permanentFocusOwner", editorRemover);
755 editorRemover = null;
756 unconfigureEnclosingScrollPane();
757 super.removeNotify();
758 }
759
760 /**
761 * Reverses the effect of <code>configureEnclosingScrollPane</code>
762 * by replacing the <code>columnHeaderView</code> of the enclosing
763 * scroll pane with <code>null</code>. <code>JTable</code>'s
764 * <code>removeNotify</code> method calls
765 * this method, which is protected so that this default uninstallation
766 * procedure can be overridden by a subclass.
767 *
768 * @see #removeNotify
769 * @see #configureEnclosingScrollPane
770 * @since 1.3
771 */
772 protected void unconfigureEnclosingScrollPane() {
773 Container p = getParent();
774 if (p instanceof JViewport) {
775 Container gp = p.getParent();
776 if (gp instanceof JScrollPane) {
777 JScrollPane scrollPane = (JScrollPane)gp;
778 // Make certain we are the viewPort's view and not, for
779 // example, the rowHeaderView of the scrollPane -
780 // an implementor of fixed columns might do this.
781 JViewport viewport = scrollPane.getViewport();
782 if (viewport == null || viewport.getView() != this) {
783 return;
784 }
785 scrollPane.setColumnHeaderView(null);
786 }
787 }
788 }
789
790 void setUIProperty(String propertyName, Object value) {
791 if (propertyName == "rowHeight") {
792 if (!isRowHeightSet) {
793 setRowHeight(((Number)value).intValue());
794 isRowHeightSet = false;
795 }
796 return;
797 }
798 super.setUIProperty(propertyName, value);
799 }
800
801 //
802 // Static Methods
803 //
804
805 /**
806 * Equivalent to <code>new JScrollPane(aTable)</code>.
807 *
808 * @deprecated As of Swing version 1.0.2,
809 * replaced by <code>new JScrollPane(aTable)</code>.
810 */
811 @Deprecated
812 static public JScrollPane createScrollPaneForTable(JTable aTable) {
813 return new JScrollPane(aTable);
814 }
815
816 //
817 // Table Attributes
818 //
819
820 /**
821 * Sets the <code>tableHeader</code> working with this <code>JTable</code> to <code>newHeader</code>.
822 * It is legal to have a <code>null</code> <code>tableHeader</code>.
823 *
824 * @param tableHeader new tableHeader
825 * @see #getTableHeader
826 * @beaninfo
827 * bound: true
828 * description: The JTableHeader instance which renders the column headers.
829 */
830 public void setTableHeader(JTableHeader tableHeader) {
831 if (this.tableHeader != tableHeader) {
832 JTableHeader old = this.tableHeader;
833 // Release the old header
834 if (old != null) {
835 old.setTable(null);
836 }
837 this.tableHeader = tableHeader;
838 if (tableHeader != null) {
839 tableHeader.setTable(this);
840 }
841 firePropertyChange("tableHeader", old, tableHeader);
842 }
843 }
844
845 /**
846 * Returns the <code>tableHeader</code> used by this <code>JTable</code>.
847 *
848 * @return the <code>tableHeader</code> used by this table
849 * @see #setTableHeader
850 */
851 public JTableHeader getTableHeader() {
852 return tableHeader;
853 }
854
855 /**
856 * Sets the height, in pixels, of all cells to <code>rowHeight</code>,
857 * revalidates, and repaints.
858 * The height of the cells will be equal to the row height minus
859 * the row margin.
860 *
861 * @param rowHeight new row height
862 * @exception IllegalArgumentException if <code>rowHeight</code> is
863 * less than 1
864 * @see #getRowHeight
865 * @beaninfo
866 * bound: true
867 * description: The height of the specified row.
868 */
869 public void setRowHeight(int rowHeight) {
870 if (rowHeight <= 0) {
871 throw new IllegalArgumentException("New row height less than 1");
872 }
873 int old = this.rowHeight;
874 this.rowHeight = rowHeight;
875 rowModel = null;
876 if (sortManager != null) {
877 sortManager.modelRowSizes = null;
878 }
879 isRowHeightSet = true;
880 resizeAndRepaint();
881 firePropertyChange("rowHeight", old, rowHeight);
882 }
883
884 /**
885 * Returns the height of a table row, in pixels.
886 * The default row height is 16.0.
887 *
888 * @return the height in pixels of a table row
889 * @see #setRowHeight
890 */
891 public int getRowHeight() {
892 return rowHeight;
893 }
894
895 private SizeSequence getRowModel() {
896 if (rowModel == null) {
897 rowModel = new SizeSequence(getRowCount(), getRowHeight());
898 }
899 return rowModel;
900 }
901
902 /**
903 * Sets the height for <code>row</code> to <code>rowHeight</code>,
904 * revalidates, and repaints. The height of the cells in this row
905 * will be equal to the row height minus the row margin.
906 *
907 * @param row the row whose height is being
908 changed
909 * @param rowHeight new row height, in pixels
910 * @exception IllegalArgumentException if <code>rowHeight</code> is
911 * less than 1
912 * @beaninfo
913 * bound: true
914 * description: The height in pixels of the cells in <code>row</code>
915 * @since 1.3
916 */
917 public void setRowHeight(int row, int rowHeight) {
918 if (rowHeight <= 0) {
919 throw new IllegalArgumentException("New row height less than 1");
920 }
921 getRowModel().setSize(row, rowHeight);
922 if (sortManager != null) {
923 sortManager.setViewRowHeight(row, rowHeight);
924 }
925 resizeAndRepaint();
926 }
927
928 /**
929 * Returns the height, in pixels, of the cells in <code>row</code>.
930 * @param row the row whose height is to be returned
931 * @return the height, in pixels, of the cells in the row
932 * @since 1.3
933 */
934 public int getRowHeight(int row) {
935 return (rowModel == null) ? getRowHeight() : rowModel.getSize(row);
936 }
937
938 /**
939 * Sets the amount of empty space between cells in adjacent rows.
940 *
941 * @param rowMargin the number of pixels between cells in a row
942 * @see #getRowMargin
943 * @beaninfo
944 * bound: true
945 * description: The amount of space between cells.
946 */
947 public void setRowMargin(int rowMargin) {
948 int old = this.rowMargin;
949 this.rowMargin = rowMargin;
950 resizeAndRepaint();
951 firePropertyChange("rowMargin", old, rowMargin);
952 }
953
954 /**
955 * Gets the amount of empty space, in pixels, between cells. Equivalent to:
956 * <code>getIntercellSpacing().height</code>.
957 * @return the number of pixels between cells in a row
958 *
959 * @see #setRowMargin
960 */
961 public int getRowMargin() {
962 return rowMargin;
963 }
964
965 /**
966 * Sets the <code>rowMargin</code> and the <code>columnMargin</code> --
967 * the height and width of the space between cells -- to
968 * <code>intercellSpacing</code>.
969 *
970 * @param intercellSpacing a <code>Dimension</code>
971 * specifying the new width
972 * and height between cells
973 * @see #getIntercellSpacing
974 * @beaninfo
975 * description: The spacing between the cells,
976 * drawn in the background color of the JTable.
977 */
978 public void setIntercellSpacing(Dimension intercellSpacing) {
979 // Set the rowMargin here and columnMargin in the TableColumnModel
980 setRowMargin(intercellSpacing.height);
981 getColumnModel().setColumnMargin(intercellSpacing.width);
982
983 resizeAndRepaint();
984 }
985
986 /**
987 * Returns the horizontal and vertical space between cells.
988 * The default spacing is (1, 1), which provides room to draw the grid.
989 *
990 * @return the horizontal and vertical spacing between cells
991 * @see #setIntercellSpacing
992 */
993 public Dimension getIntercellSpacing() {
994 return new Dimension(getColumnModel().getColumnMargin(), rowMargin);
995 }
996
997 /**
998 * Sets the color used to draw grid lines to <code>gridColor</code> and redisplays.
999 * The default color is look and feel dependent.
1000 *
1001 * @param gridColor the new color of the grid lines
1002 * @exception IllegalArgumentException if <code>gridColor</code> is <code>null</code>
1003 * @see #getGridColor
1004 * @beaninfo
1005 * bound: true
1006 * description: The grid color.
1007 */
1008 public void setGridColor(Color gridColor) {
1009 if (gridColor == null) {
1010 throw new IllegalArgumentException("New color is null");
1011 }
1012 Color old = this.gridColor;
1013 this.gridColor = gridColor;
1014 firePropertyChange("gridColor", old, gridColor);
1015 // Redraw
1016 repaint();
1017 }
1018
1019 /**
1020 * Returns the color used to draw grid lines.
1021 * The default color is look and feel dependent.
1022 *
1023 * @return the color used to draw grid lines
1024 * @see #setGridColor
1025 */
1026 public Color getGridColor() {
1027 return gridColor;
1028 }
1029
1030 /**
1031 * Sets whether the table draws grid lines around cells.
1032 * If <code>showGrid</code> is true it does; if it is false it doesn't.
1033 * There is no <code>getShowGrid</code> method as this state is held
1034 * in two variables -- <code>showHorizontalLines</code> and <code>showVerticalLines</code> --
1035 * each of which can be queried independently.
1036 *
1037 * @param showGrid true if table view should draw grid lines
1038 *
1039 * @see #setShowVerticalLines
1040 * @see #setShowHorizontalLines
1041 * @beaninfo
1042 * description: The color used to draw the grid lines.
1043 */
1044 public void setShowGrid(boolean showGrid) {
1045 setShowHorizontalLines(showGrid);
1046 setShowVerticalLines(showGrid);
1047
1048 // Redraw
1049 repaint();
1050 }
1051
1052 /**
1053 * Sets whether the table draws horizontal lines between cells.
1054 * If <code>showHorizontalLines</code> is true it does; if it is false it doesn't.
1055 *
1056 * @param showHorizontalLines true if table view should draw horizontal lines
1057 * @see #getShowHorizontalLines
1058 * @see #setShowGrid
1059 * @see #setShowVerticalLines
1060 * @beaninfo
1061 * bound: true
1062 * description: Whether horizontal lines should be drawn in between the cells.
1063 */
1064 public void setShowHorizontalLines(boolean showHorizontalLines) {
1065 boolean old = this.showHorizontalLines;
1066 this.showHorizontalLines = showHorizontalLines;
1067 firePropertyChange("showHorizontalLines", old, showHorizontalLines);
1068
1069 // Redraw
1070 repaint();
1071 }
1072
1073 /**
1074 * Sets whether the table draws vertical lines between cells.
1075 * If <code>showVerticalLines</code> is true it does; if it is false it doesn't.
1076 *
1077 * @param showVerticalLines true if table view should draw vertical lines
1078 * @see #getShowVerticalLines
1079 * @see #setShowGrid
1080 * @see #setShowHorizontalLines
1081 * @beaninfo
1082 * bound: true
1083 * description: Whether vertical lines should be drawn in between the cells.
1084 */
1085 public void setShowVerticalLines(boolean showVerticalLines) {
1086 boolean old = this.showVerticalLines;
1087 this.showVerticalLines = showVerticalLines;
1088 firePropertyChange("showVerticalLines", old, showVerticalLines);
1089 // Redraw
1090 repaint();
1091 }
1092
1093 /**
1094 * Returns true if the table draws horizontal lines between cells, false if it
1095 * doesn't. The default is true.
1096 *
1097 * @return true if the table draws horizontal lines between cells, false if it
1098 * doesn't
1099 * @see #setShowHorizontalLines
1100 */
1101 public boolean getShowHorizontalLines() {
1102 return showHorizontalLines;
1103 }
1104
1105 /**
1106 * Returns true if the table draws vertical lines between cells, false if it
1107 * doesn't. The default is true.
1108 *
1109 * @return true if the table draws vertical lines between cells, false if it
1110 * doesn't
1111 * @see #setShowVerticalLines
1112 */
1113 public boolean getShowVerticalLines() {
1114 return showVerticalLines;
1115 }
1116
1117 /**
1118 * Sets the table's auto resize mode when the table is resized. For further
1119 * information on how the different resize modes work, see
1120 * {@link #doLayout}.
1121 *
1122 * @param mode One of 5 legal values:
1123 * AUTO_RESIZE_OFF,
1124 * AUTO_RESIZE_NEXT_COLUMN,
1125 * AUTO_RESIZE_SUBSEQUENT_COLUMNS,
1126 * AUTO_RESIZE_LAST_COLUMN,
1127 * AUTO_RESIZE_ALL_COLUMNS
1128 *
1129 * @see #getAutoResizeMode
1130 * @see #doLayout
1131 * @beaninfo
1132 * bound: true
1133 * description: Whether the columns should adjust themselves automatically.
1134 * enum: AUTO_RESIZE_OFF JTable.AUTO_RESIZE_OFF
1135 * AUTO_RESIZE_NEXT_COLUMN JTable.AUTO_RESIZE_NEXT_COLUMN
1136 * AUTO_RESIZE_SUBSEQUENT_COLUMNS JTable.AUTO_RESIZE_SUBSEQUENT_COLUMNS
1137 * AUTO_RESIZE_LAST_COLUMN JTable.AUTO_RESIZE_LAST_COLUMN
1138 * AUTO_RESIZE_ALL_COLUMNS JTable.AUTO_RESIZE_ALL_COLUMNS
1139 */
1140 public void setAutoResizeMode(int mode) {
1141 if ((mode == AUTO_RESIZE_OFF) ||
1142 (mode == AUTO_RESIZE_NEXT_COLUMN) ||
1143 (mode == AUTO_RESIZE_SUBSEQUENT_COLUMNS) ||
1144 (mode == AUTO_RESIZE_LAST_COLUMN) ||
1145 (mode == AUTO_RESIZE_ALL_COLUMNS)) {
1146 int old = autoResizeMode;
1147 autoResizeMode = mode;
1148 resizeAndRepaint();
1149 if (tableHeader != null) {
1150 tableHeader.resizeAndRepaint();
1151 }
1152 firePropertyChange("autoResizeMode", old, autoResizeMode);
1153 }
1154 }
1155
1156 /**
1157 * Returns the auto resize mode of the table. The default mode
1158 * is AUTO_RESIZE_SUBSEQUENT_COLUMNS.
1159 *
1160 * @return the autoResizeMode of the table
1161 *
1162 * @see #setAutoResizeMode
1163 * @see #doLayout
1164 */
1165 public int getAutoResizeMode() {
1166 return autoResizeMode;
1167 }
1168
1169 /**
1170 * Sets this table's <code>autoCreateColumnsFromModel</code> flag.
1171 * This method calls <code>createDefaultColumnsFromModel</code> if
1172 * <code>autoCreateColumnsFromModel</code> changes from false to true.
1173 *
1174 * @param autoCreateColumnsFromModel true if <code>JTable</code> should automatically create columns
1175 * @see #getAutoCreateColumnsFromModel
1176 * @see #createDefaultColumnsFromModel
1177 * @beaninfo
1178 * bound: true
1179 * description: Automatically populates the columnModel when a new TableModel is submitted.
1180 */
1181 public void setAutoCreateColumnsFromModel(boolean autoCreateColumnsFromModel) {
1182 if (this.autoCreateColumnsFromModel != autoCreateColumnsFromModel) {
1183 boolean old = this.autoCreateColumnsFromModel;
1184 this.autoCreateColumnsFromModel = autoCreateColumnsFromModel;
1185 if (autoCreateColumnsFromModel) {
1186 createDefaultColumnsFromModel();
1187 }
1188 firePropertyChange("autoCreateColumnsFromModel", old, autoCreateColumnsFromModel);
1189 }
1190 }
1191
1192 /**
1193 * Determines whether the table will create default columns from the model.
1194 * If true, <code>setModel</code> will clear any existing columns and
1195 * create new columns from the new model. Also, if the event in
1196 * the <code>tableChanged</code> notification specifies that the
1197 * entire table changed, then the columns will be rebuilt.
1198 * The default is true.
1199 *
1200 * @return the autoCreateColumnsFromModel of the table
1201 * @see #setAutoCreateColumnsFromModel
1202 * @see #createDefaultColumnsFromModel
1203 */
1204 public boolean getAutoCreateColumnsFromModel() {
1205 return autoCreateColumnsFromModel;
1206 }
1207
1208 /**
1209 * Creates default columns for the table from
1210 * the data model using the <code>getColumnCount</code> method
1211 * defined in the <code>TableModel</code> interface.
1212 * <p>
1213 * Clears any existing columns before creating the
1214 * new columns based on information from the model.
1215 *
1216 * @see #getAutoCreateColumnsFromModel
1217 */
1218 public void createDefaultColumnsFromModel() {
1219 TableModel m = getModel();
1220 if (m != null) {
1221 // Remove any current columns
1222 TableColumnModel cm = getColumnModel();
1223 while (cm.getColumnCount() > 0) {
1224 cm.removeColumn(cm.getColumn(0));
1225 }
1226
1227 // Create new columns from the data model info
1228 for (int i = 0; i < m.getColumnCount(); i++) {
1229 TableColumn newColumn = new TableColumn(i);
1230 addColumn(newColumn);
1231 }
1232 }
1233 }
1234
1235 /**
1236 * Sets a default cell renderer to be used if no renderer has been set in
1237 * a <code>TableColumn</code>. If renderer is <code>null</code>,
1238 * removes the default renderer for this column class.
1239 *
1240 * @param columnClass set the default cell renderer for this columnClass
1241 * @param renderer default cell renderer to be used for this
1242 * columnClass
1243 * @see #getDefaultRenderer
1244 * @see #setDefaultEditor
1245 */
1246 public void setDefaultRenderer(Class<?> columnClass, TableCellRenderer renderer) {
1247 if (renderer != null) {
1248 defaultRenderersByColumnClass.put(columnClass, renderer);
1249 }
1250 else {
1251 defaultRenderersByColumnClass.remove(columnClass);
1252 }
1253 }
1254
1255 /**
1256 * Returns the cell renderer to be used when no renderer has been set in
1257 * a <code>TableColumn</code>. During the rendering of cells the renderer is fetched from
1258 * a <code>Hashtable</code> of entries according to the class of the cells in the column. If
1259 * there is no entry for this <code>columnClass</code> the method returns
1260 * the entry for the most specific superclass. The <code>JTable</code> installs entries
1261 * for <code>Object</code>, <code>Number</code>, and <code>Boolean</code>, all of which can be modified
1262 * or replaced.
1263 *
1264 * @param columnClass return the default cell renderer
1265 * for this columnClass
1266 * @return the renderer for this columnClass
1267 * @see #setDefaultRenderer
1268 * @see #getColumnClass
1269 */
1270 public TableCellRenderer getDefaultRenderer(Class<?> columnClass) {
1271 if (columnClass == null) {
1272 return null;
1273 }
1274 else {
1275 Object renderer = defaultRenderersByColumnClass.get(columnClass);
1276 if (renderer != null) {
1277 return (TableCellRenderer)renderer;
1278 }
1279 else {
1280 return getDefaultRenderer(columnClass.getSuperclass());
1281 }
1282 }
1283 }
1284
1285 /**
1286 * Sets a default cell editor to be used if no editor has been set in
1287 * a <code>TableColumn</code>. If no editing is required in a table, or a
1288 * particular column in a table, uses the <code>isCellEditable</code>
1289 * method in the <code>TableModel</code> interface to ensure that this
1290 * <code>JTable</code> will not start an editor in these columns.
1291 * If editor is <code>null</code>, removes the default editor for this
1292 * column class.
1293 *
1294 * @param columnClass set the default cell editor for this columnClass
1295 * @param editor default cell editor to be used for this columnClass
1296 * @see TableModel#isCellEditable
1297 * @see #getDefaultEditor
1298 * @see #setDefaultRenderer
1299 */
1300 public void setDefaultEditor(Class<?> columnClass, TableCellEditor editor) {
1301 if (editor != null) {
1302 defaultEditorsByColumnClass.put(columnClass, editor);
1303 }
1304 else {
1305 defaultEditorsByColumnClass.remove(columnClass);
1306 }
1307 }
1308
1309 /**
1310 * Returns the editor to be used when no editor has been set in
1311 * a <code>TableColumn</code>. During the editing of cells the editor is fetched from
1312 * a <code>Hashtable</code> of entries according to the class of the cells in the column. If
1313 * there is no entry for this <code>columnClass</code> the method returns
1314 * the entry for the most specific superclass. The <code>JTable</code> installs entries
1315 * for <code>Object</code>, <code>Number</code>, and <code>Boolean</code>, all of which can be modified
1316 * or replaced.
1317 *
1318 * @param columnClass return the default cell editor for this columnClass
1319 * @return the default cell editor to be used for this columnClass
1320 * @see #setDefaultEditor
1321 * @see #getColumnClass
1322 */
1323 public TableCellEditor getDefaultEditor(Class<?> columnClass) {
1324 if (columnClass == null) {
1325 return null;
1326 }
1327 else {
1328 Object editor = defaultEditorsByColumnClass.get(columnClass);
1329 if (editor != null) {
1330 return (TableCellEditor)editor;
1331 }
1332 else {
1333 return getDefaultEditor(columnClass.getSuperclass());
1334 }
1335 }
1336 }
1337
1338 /**
1339 * Turns on or off automatic drag handling. In order to enable automatic
1340 * drag handling, this property should be set to {@code true}, and the
1341 * table's {@code TransferHandler} needs to be {@code non-null}.
1342 * The default value of the {@code dragEnabled} property is {@code false}.
1343 * <p>
1344 * The job of honoring this property, and recognizing a user drag gesture,
1345 * lies with the look and feel implementation, and in particular, the table's
1346 * {@code TableUI}. When automatic drag handling is enabled, most look and
1347 * feels (including those that subclass {@code BasicLookAndFeel}) begin a
1348 * drag and drop operation whenever the user presses the mouse button over
1349 * an item (in single selection mode) or a selection (in other selection
1350 * modes) and then moves the mouse a few pixels. Setting this property to
1351 * {@code true} can therefore have a subtle effect on how selections behave.
1352 * <p>
1353 * If a look and feel is used that ignores this property, you can still
1354 * begin a drag and drop operation by calling {@code exportAsDrag} on the
1355 * table's {@code TransferHandler}.
1356 *
1357 * @param b whether or not to enable automatic drag handling
1358 * @exception HeadlessException if
1359 * <code>b</code> is <code>true</code> and
1360 * <code>GraphicsEnvironment.isHeadless()</code>
1361 * returns <code>true</code>
1362 * @see java.awt.GraphicsEnvironment#isHeadless
1363 * @see #getDragEnabled
1364 * @see #setTransferHandler
1365 * @see TransferHandler
1366 * @since 1.4
1367 *
1368 * @beaninfo
1369 * description: determines whether automatic drag handling is enabled
1370 * bound: false
1371 */
1372 public void setDragEnabled(boolean b) {
1373 if (b && GraphicsEnvironment.isHeadless()) {
1374 throw new HeadlessException();
1375 }
1376 dragEnabled = b;
1377 }
1378
1379 /**
1380 * Returns whether or not automatic drag handling is enabled.
1381 *
1382 * @return the value of the {@code dragEnabled} property
1383 * @see #setDragEnabled
1384 * @since 1.4
1385 */
1386 public boolean getDragEnabled() {
1387 return dragEnabled;
1388 }
1389
1390 /**
1391 * Sets the drop mode for this component. For backward compatibility,
1392 * the default for this property is <code>DropMode.USE_SELECTION</code>.
1393 * Usage of one of the other modes is recommended, however, for an
1394 * improved user experience. <code>DropMode.ON</code>, for instance,
1395 * offers similar behavior of showing items as selected, but does so without
1396 * affecting the actual selection in the table.
1397 * <p>
1398 * <code>JTable</code> supports the following drop modes:
1399 * <ul>
1400 * <li><code>DropMode.USE_SELECTION</code></li>
1401 * <li><code>DropMode.ON</code></li>
1402 * <li><code>DropMode.INSERT</code></li>
1403 * <li><code>DropMode.INSERT_ROWS</code></li>
1404 * <li><code>DropMode.INSERT_COLS</code></li>
1405 * <li><code>DropMode.ON_OR_INSERT</code></li>
1406 * <li><code>DropMode.ON_OR_INSERT_ROWS</code></li>
1407 * <li><code>DropMode.ON_OR_INSERT_COLS</code></li>
1408 * </ul>
1409 * <p>
1410 * The drop mode is only meaningful if this component has a
1411 * <code>TransferHandler</code> that accepts drops.
1412 *
1413 * @param dropMode the drop mode to use
1414 * @throws IllegalArgumentException if the drop mode is unsupported
1415 * or <code>null</code>
1416 * @see #getDropMode
1417 * @see #getDropLocation
1418 * @see #setTransferHandler
1419 * @see TransferHandler
1420 * @since 1.6
1421 */
1422 public final void setDropMode(DropMode dropMode) {
1423 if (dropMode != null) {
1424 switch (dropMode) {
1425 case USE_SELECTION:
1426 case ON:
1427 case INSERT:
1428 case INSERT_ROWS:
1429 case INSERT_COLS:
1430 case ON_OR_INSERT:
1431 case ON_OR_INSERT_ROWS:
1432 case ON_OR_INSERT_COLS:
1433 this.dropMode = dropMode;
1434 return;
1435 }
1436 }
1437
1438 throw new IllegalArgumentException(dropMode + ": Unsupported drop mode for table");
1439 }
1440
1441 /**
1442 * Returns the drop mode for this component.
1443 *
1444 * @return the drop mode for this component
1445 * @see #setDropMode
1446 * @since 1.6
1447 */
1448 public final DropMode getDropMode() {
1449 return dropMode;
1450 }
1451
1452 /**
1453 * Calculates a drop location in this component, representing where a
1454 * drop at the given point should insert data.
1455 *
1456 * @param p the point to calculate a drop location for
1457 * @return the drop location, or <code>null</code>
1458 */
1459 DropLocation dropLocationForPoint(Point p) {
1460 DropLocation location = null;
1461
1462 int row = rowAtPoint(p);
1463 int col = columnAtPoint(p);
1464 boolean outside = Boolean.TRUE == getClientProperty("Table.isFileList")
1465 && SwingUtilities2.pointOutsidePrefSize(this, row, col, p);
1466
1467 Rectangle rect = getCellRect(row, col, true);
1468 Section xSection, ySection;
1469 boolean between = false;
1470 boolean ltr = getComponentOrientation().isLeftToRight();
1471
1472 switch(dropMode) {
1473 case USE_SELECTION:
1474 case ON:
1475 if (row == -1 || col == -1 || outside) {
1476 location = new DropLocation(p, -1, -1, false, false);
1477 } else {
1478 location = new DropLocation(p, row, col, false, false);
1479 }
1480 break;
1481 case INSERT:
1482 if (row == -1 && col == -1) {
1483 location = new DropLocation(p, 0, 0, true, true);
1484 break;
1485 }
1486
1487 xSection = SwingUtilities2.liesInHorizontal(rect, p, ltr, true);
1488
1489 if (row == -1) {
1490 if (xSection == LEADING) {
1491 location = new DropLocation(p, getRowCount(), col, true, true);
1492 } else if (xSection == TRAILING) {
1493 location = new DropLocation(p, getRowCount(), col + 1, true, true);
1494 } else {
1495 location = new DropLocation(p, getRowCount(), col, true, false);
1496 }
1497 } else if (xSection == LEADING || xSection == TRAILING) {
1498 ySection = SwingUtilities2.liesInVertical(rect, p, true);
1499 if (ySection == LEADING) {
1500 between = true;
1501 } else if (ySection == TRAILING) {
1502 row++;
1503 between = true;
1504 }
1505
1506 location = new DropLocation(p, row,
1507 xSection == TRAILING ? col + 1 : col,
1508 between, true);
1509 } else {
1510 if (SwingUtilities2.liesInVertical(rect, p, false) == TRAILING) {
1511 row++;
1512 }
1513
1514 location = new DropLocation(p, row, col, true, false);
1515 }
1516
1517 break;
1518 case INSERT_ROWS:
1519 if (row == -1 && col == -1) {
1520 location = new DropLocation(p, -1, -1, false, false);
1521 break;
1522 }
1523
1524 if (row == -1) {
1525 location = new DropLocation(p, getRowCount(), col, true, false);
1526 break;
1527 }
1528
1529 if (SwingUtilities2.liesInVertical(rect, p, false) == TRAILING) {
1530 row++;
1531 }
1532
1533 location = new DropLocation(p, row, col, true, false);
1534 break;
1535 case ON_OR_INSERT_ROWS:
1536 if (row == -1 && col == -1) {
1537 location = new DropLocation(p, -1, -1, false, false);
1538 break;
1539 }
1540
1541 if (row == -1) {
1542 location = new DropLocation(p, getRowCount(), col, true, false);
1543 break;
1544 }
1545
1546 ySection = SwingUtilities2.liesInVertical(rect, p, true);
1547 if (ySection == LEADING) {
1548 between = true;
1549 } else if (ySection == TRAILING) {
1550 row++;
1551 between = true;
1552 }
1553
1554 location = new DropLocation(p, row, col, between, false);
1555 break;
1556 case INSERT_COLS:
1557 if (row == -1) {
1558 location = new DropLocation(p, -1, -1, false, false);
1559 break;
1560 }
1561
1562 if (col == -1) {
1563 location = new DropLocation(p, getColumnCount(), col, false, true);
1564 break;
1565 }
1566
1567 if (SwingUtilities2.liesInHorizontal(rect, p, ltr, false) == TRAILING) {
1568 col++;
1569 }
1570
1571 location = new DropLocation(p, row, col, false, true);
1572 break;
1573 case ON_OR_INSERT_COLS:
1574 if (row == -1) {
1575 location = new DropLocation(p, -1, -1, false, false);
1576 break;
1577 }
1578
1579 if (col == -1) {
1580 location = new DropLocation(p, row, getColumnCount(), false, true);
1581 break;
1582 }
1583
1584 xSection = SwingUtilities2.liesInHorizontal(rect, p, ltr, true);
1585 if (xSection == LEADING) {
1586 between = true;
1587 } else if (xSection == TRAILING) {
1588 col++;
1589 between = true;
1590 }
1591
1592 location = new DropLocation(p, row, col, false, between);
1593 break;
1594 case ON_OR_INSERT:
1595 if (row == -1 && col == -1) {
1596 location = new DropLocation(p, 0, 0, true, true);
1597 break;
1598 }
1599
1600 xSection = SwingUtilities2.liesInHorizontal(rect, p, ltr, true);
1601
1602 if (row == -1) {
1603 if (xSection == LEADING) {
1604 location = new DropLocation(p, getRowCount(), col, true, true);
1605 } else if (xSection == TRAILING) {
1606 location = new DropLocation(p, getRowCount(), col + 1, true, true);
1607 } else {
1608 location = new DropLocation(p, getRowCount(), col, true, false);
1609 }
1610
1611 break;
1612 }
1613
1614 ySection = SwingUtilities2.liesInVertical(rect, p, true);
1615 if (ySection == LEADING) {
1616 between = true;
1617 } else if (ySection == TRAILING) {
1618 row++;
1619 between = true;
1620 }
1621
1622 location = new DropLocation(p, row,
1623 xSection == TRAILING ? col + 1 : col,
1624 between,
1625 xSection != MIDDLE);
1626
1627 break;
1628 default:
1629 assert false : "Unexpected drop mode";
1630 }
1631
1632 return location;
1633 }
1634
1635 /**
1636 * Called to set or clear the drop location during a DnD operation.
1637 * In some cases, the component may need to use it's internal selection
1638 * temporarily to indicate the drop location. To help facilitate this,
1639 * this method returns and accepts as a parameter a state object.
1640 * This state object can be used to store, and later restore, the selection
1641 * state. Whatever this method returns will be passed back to it in
1642 * future calls, as the state parameter. If it wants the DnD system to
1643 * continue storing the same state, it must pass it back every time.
1644 * Here's how this is used:
1645 * <p>
1646 * Let's say that on the first call to this method the component decides
1647 * to save some state (because it is about to use the selection to show
1648 * a drop index). It can return a state object to the caller encapsulating
1649 * any saved selection state. On a second call, let's say the drop location
1650 * is being changed to something else. The component doesn't need to
1651 * restore anything yet, so it simply passes back the same state object
1652 * to have the DnD system continue storing it. Finally, let's say this
1653 * method is messaged with <code>null</code>. This means DnD
1654 * is finished with this component for now, meaning it should restore
1655 * state. At this point, it can use the state parameter to restore
1656 * said state, and of course return <code>null</code> since there's
1657 * no longer anything to store.
1658 *
1659 * @param location the drop location (as calculated by
1660 * <code>dropLocationForPoint</code>) or <code>null</code>
1661 * if there's no longer a valid drop location
1662 * @param state the state object saved earlier for this component,
1663 * or <code>null</code>
1664 * @param forDrop whether or not the method is being called because an
1665 * actual drop occurred
1666 * @return any saved state for this component, or <code>null</code> if none
1667 */
1668 Object setDropLocation(TransferHandler.DropLocation location,
1669 Object state,
1670 boolean forDrop) {
1671
1672 Object retVal = null;
1673 DropLocation tableLocation = (DropLocation)location;
1674
1675 if (dropMode == DropMode.USE_SELECTION) {
1676 if (tableLocation == null) {
1677 if (!forDrop && state != null) {
1678 clearSelection();
1679
1680 int[] rows = (int[])((int[][])state)[0];
1681 int[] cols = (int[])((int[][])state)[1];
1682 int[] anchleads = (int[])((int[][])state)[2];
1683
1684 for (int i = 0; i < rows.length; i++) {
1685 addRowSelectionInterval(rows[i], rows[i]);
1686 }
1687
1688 for (int i = 0; i < cols.length; i++) {
1689 addColumnSelectionInterval(cols[i], cols[i]);
1690 }
1691
1692 SwingUtilities2.setLeadAnchorWithoutSelection(
1693 getSelectionModel(), anchleads[1], anchleads[0]);
1694
1695 SwingUtilities2.setLeadAnchorWithoutSelection(
1696 getColumnModel().getSelectionModel(),
1697 anchleads[3], anchleads[2]);
1698 }
1699 } else {
1700 if (dropLocation == null) {
1701 retVal = new int[][]{
1702 getSelectedRows(),
1703 getSelectedColumns(),
1704 {getAdjustedIndex(getSelectionModel()
1705 .getAnchorSelectionIndex(), true),
1706 getAdjustedIndex(getSelectionModel()
1707 .getLeadSelectionIndex(), true),
1708 getAdjustedIndex(getColumnModel().getSelectionModel()
1709 .getAnchorSelectionIndex(), false),
1710 getAdjustedIndex(getColumnModel().getSelectionModel()
1711 .getLeadSelectionIndex(), false)}};
1712 } else {
1713 retVal = state;
1714 }
1715
1716 if (tableLocation.getRow() == -1) {
1717 clearSelectionAndLeadAnchor();
1718 } else {
1719 setRowSelectionInterval(tableLocation.getRow(),
1720 tableLocation.getRow());
1721 setColumnSelectionInterval(tableLocation.getColumn(),
1722 tableLocation.getColumn());
1723 }
1724 }
1725 }
1726
1727 DropLocation old = dropLocation;
1728 dropLocation = tableLocation;
1729 firePropertyChange("dropLocation", old, dropLocation);
1730
1731 return retVal;
1732 }
1733
1734 /**
1735 * Returns the location that this component should visually indicate
1736 * as the drop location during a DnD operation over the component,
1737 * or {@code null} if no location is to currently be shown.
1738 * <p>
1739 * This method is not meant for querying the drop location
1740 * from a {@code TransferHandler}, as the drop location is only
1741 * set after the {@code TransferHandler}'s <code>canImport</code>
1742 * has returned and has allowed for the location to be shown.
1743 * <p>
1744 * When this property changes, a property change event with
1745 * name "dropLocation" is fired by the component.
1746 *
1747 * @return the drop location
1748 * @see #setDropMode
1749 * @see TransferHandler#canImport(TransferHandler.TransferSupport)
1750 * @since 1.6
1751 */
1752 public final DropLocation getDropLocation() {
1753 return dropLocation;
1754 }
1755
1756 /**
1757 * Specifies whether a {@code RowSorter} should be created for the
1758 * table whenever its model changes.
1759 * <p>
1760 * When {@code setAutoCreateRowSorter(true)} is invoked, a {@code
1761 * TableRowSorter} is immediately created and installed on the
1762 * table. While the {@code autoCreateRowSorter} property remains
1763 * {@code true}, every time the model is changed, a new {@code
1764 * TableRowSorter} is created and set as the table's row sorter.
1765 *
1766 * @param autoCreateRowSorter whether or not a {@code RowSorter}
1767 * should be automatically created
1768 * @see javax.swing.table.TableRowSorter
1769 * @beaninfo
1770 * bound: true
1771 * preferred: true
1772 * description: Whether or not to turn on sorting by default.
1773 * @since 1.6
1774 */
1775 public void setAutoCreateRowSorter(boolean autoCreateRowSorter) {
1776 boolean oldValue = this.autoCreateRowSorter;
1777 this.autoCreateRowSorter = autoCreateRowSorter;
1778 if (autoCreateRowSorter) {
1779 setRowSorter(new TableRowSorter(getModel()));
1780 }
1781 firePropertyChange("autoCreateRowSorter", oldValue,
1782 autoCreateRowSorter);
1783 }
1784
1785 /**
1786 * Returns {@code true} if whenever the model changes, a new
1787 * {@code RowSorter} should be created and installed
1788 * as the table's sorter; otherwise, returns {@code false}.
1789 *
1790 * @return true if a {@code RowSorter} should be created when
1791 * the model changes
1792 * @since 1.6
1793 */
1794 public boolean getAutoCreateRowSorter() {
1795 return autoCreateRowSorter;
1796 }
1797
1798 /**
1799 * Specifies whether the selection should be updated after sorting.
1800 * If true, on sorting the selection is reset such that
1801 * the same rows, in terms of the model, remain selected. The default
1802 * is true.
1803 *
1804 * @param update whether or not to update the selection on sorting
1805 * @beaninfo
1806 * bound: true
1807 * expert: true
1808 * description: Whether or not to update the selection on sorting
1809 * @since 1.6
1810 */
1811 public void setUpdateSelectionOnSort(boolean update) {
1812 if (updateSelectionOnSort != update) {
1813 updateSelectionOnSort = update;
1814 firePropertyChange("updateSelectionOnSort", !update, update);
1815 }
1816 }
1817
1818 /**
1819 * Returns true if the selection should be updated after sorting.
1820 *
1821 * @return whether to update the selection on a sort
1822 * @since 1.6
1823 */
1824 public boolean getUpdateSelectionOnSort() {
1825 return updateSelectionOnSort;
1826 }
1827
1828 /**
1829 * Sets the <code>RowSorter</code>. <code>RowSorter</code> is used
1830 * to provide sorting and filtering to a <code>JTable</code>.
1831 * <p>
1832 * This method clears the selection and resets any variable row heights.
1833 * <p>
1834 * This method fires a <code>PropertyChangeEvent</code> when appropriate,
1835 * with the property name <code>"rowSorter"</code>. For
1836 * backward-compatibility, this method fires an additional event with the
1837 * property name <code>"sorter"</code>.
1838 * <p>
1839 * If the underlying model of the <code>RowSorter</code> differs from
1840 * that of this <code>JTable</code> undefined behavior will result.
1841 *
1842 * @param sorter the <code>RowSorter</code>; <code>null</code> turns
1843 * sorting off
1844 * @see javax.swing.table.TableRowSorter
1845 * @beaninfo
1846 * bound: true
1847 * description: The table's RowSorter
1848 * @since 1.6
1849 */
1850 public void setRowSorter(RowSorter<? extends TableModel> sorter) {
1851 RowSorter<? extends TableModel> oldRowSorter = null;
1852 if (sortManager != null) {
1853 oldRowSorter = sortManager.sorter;
1854 sortManager.dispose();
1855 sortManager = null;
1856 }
1857 rowModel = null;
1858 clearSelectionAndLeadAnchor();
1859 if (sorter != null) {
1860 sortManager = new SortManager(sorter);
1861 }
1862 resizeAndRepaint();
1863 firePropertyChange("rowSorter", oldRowSorter, sorter);
1864 firePropertyChange("sorter", oldRowSorter, sorter);
1865 }
1866
1867 /**
1868 * Returns the object responsible for sorting.
1869 *
1870 * @return the object responsible for sorting
1871 * @since 1.6
1872 */
1873 public RowSorter<? extends TableModel> getRowSorter() {
1874 return (sortManager != null) ? sortManager.sorter : null;
1875 }
1876
1877 //
1878 // Selection methods
1879 //
1880 /**
1881 * Sets the table's selection mode to allow only single selections, a single
1882 * contiguous interval, or multiple intervals.
1883 * <P>
1884 * <bold>Note:</bold>
1885 * <code>JTable</code> provides all the methods for handling
1886 * column and row selection. When setting states,
1887 * such as <code>setSelectionMode</code>, it not only
1888 * updates the mode for the row selection model but also sets similar
1889 * values in the selection model of the <code>columnModel</code>.
1890 * If you want to have the row and column selection models operating
1891 * in different modes, set them both directly.
1892 * <p>
1893 * Both the row and column selection models for <code>JTable</code>
1894 * default to using a <code>DefaultListSelectionModel</code>
1895 * so that <code>JTable</code> works the same way as the
1896 * <code>JList</code>. See the <code>setSelectionMode</code> method
1897 * in <code>JList</code> for details about the modes.
1898 *
1899 * @see JList#setSelectionMode
1900 * @beaninfo
1901 * description: The selection mode used by the row and column selection models.
1902 * enum: SINGLE_SELECTION ListSelectionModel.SINGLE_SELECTION
1903 * SINGLE_INTERVAL_SELECTION ListSelectionModel.SINGLE_INTERVAL_SELECTION
1904 * MULTIPLE_INTERVAL_SELECTION ListSelectionModel.MULTIPLE_INTERVAL_SELECTION
1905 */
1906 public void setSelectionMode(int selectionMode) {
1907 clearSelection();
1908 getSelectionModel().setSelectionMode(selectionMode);
1909 getColumnModel().getSelectionModel().setSelectionMode(selectionMode);
1910 }
1911
1912 /**
1913 * Sets whether the rows in this model can be selected.
1914 *
1915 * @param rowSelectionAllowed true if this model will allow row selection
1916 * @see #getRowSelectionAllowed
1917 * @beaninfo
1918 * bound: true
1919 * attribute: visualUpdate true
1920 * description: If true, an entire row is selected for each selected cell.
1921 */
1922 public void setRowSelectionAllowed(boolean rowSelectionAllowed) {
1923 boolean old = this.rowSelectionAllowed;
1924 this.rowSelectionAllowed = rowSelectionAllowed;
1925 if (old != rowSelectionAllowed) {
1926 repaint();
1927 }
1928 firePropertyChange("rowSelectionAllowed", old, rowSelectionAllowed);
1929 }
1930
1931 /**
1932 * Returns true if rows can be selected.
1933 *
1934 * @return true if rows can be selected, otherwise false
1935 * @see #setRowSelectionAllowed
1936 */
1937 public boolean getRowSelectionAllowed() {
1938 return rowSelectionAllowed;
1939 }
1940
1941 /**
1942 * Sets whether the columns in this model can be selected.
1943 *
1944 * @param columnSelectionAllowed true if this model will allow column selection
1945 * @see #getColumnSelectionAllowed
1946 * @beaninfo
1947 * bound: true
1948 * attribute: visualUpdate true
1949 * description: If true, an entire column is selected for each selected cell.
1950 */
1951 public void setColumnSelectionAllowed(boolean columnSelectionAllowed) {
1952 boolean old = columnModel.getColumnSelectionAllowed();
1953 columnModel.setColumnSelectionAllowed(columnSelectionAllowed);
1954 if (old != columnSelectionAllowed) {
1955 repaint();
1956 }
1957 firePropertyChange("columnSelectionAllowed", old, columnSelectionAllowed);
1958 }
1959
1960 /**
1961 * Returns true if columns can be selected.
1962 *
1963 * @return true if columns can be selected, otherwise false
1964 * @see #setColumnSelectionAllowed
1965 */
1966 public boolean getColumnSelectionAllowed() {
1967 return columnModel.getColumnSelectionAllowed();
1968 }
1969
1970 /**
1971 * Sets whether this table allows both a column selection and a
1972 * row selection to exist simultaneously. When set,
1973 * the table treats the intersection of the row and column selection
1974 * models as the selected cells. Override <code>isCellSelected</code> to
1975 * change this default behavior. This method is equivalent to setting
1976 * both the <code>rowSelectionAllowed</code> property and
1977 * <code>columnSelectionAllowed</code> property of the
1978 * <code>columnModel</code> to the supplied value.
1979 *
1980 * @param cellSelectionEnabled true if simultaneous row and column
1981 * selection is allowed
1982 * @see #getCellSelectionEnabled
1983 * @see #isCellSelected
1984 * @beaninfo
1985 * bound: true
1986 * attribute: visualUpdate true
1987 * description: Select a rectangular region of cells rather than
1988 * rows or columns.
1989 */
1990 public void setCellSelectionEnabled(boolean cellSelectionEnabled) {
1991 setRowSelectionAllowed(cellSelectionEnabled);
1992 setColumnSelectionAllowed(cellSelectionEnabled);
1993 boolean old = this.cellSelectionEnabled;
1994 this.cellSelectionEnabled = cellSelectionEnabled;
1995 firePropertyChange("cellSelectionEnabled", old, cellSelectionEnabled);
1996 }
1997
1998 /**
1999 * Returns true if both row and column selection models are enabled.
2000 * Equivalent to <code>getRowSelectionAllowed() &&
2001 * getColumnSelectionAllowed()</code>.
2002 *
2003 * @return true if both row and column selection models are enabled
2004 *
2005 * @see #setCellSelectionEnabled
2006 */
2007 public boolean getCellSelectionEnabled() {
2008 return getRowSelectionAllowed() && getColumnSelectionAllowed();
2009 }
2010
2011 /**
2012 * Selects all rows, columns, and cells in the table.
2013 */
2014 public void selectAll() {
2015 // If I'm currently editing, then I should stop editing
2016 if (isEditing()) {
2017 removeEditor();
2018 }
2019 if (getRowCount() > 0 && getColumnCount() > 0) {
2020 int oldLead;
2021 int oldAnchor;
2022 ListSelectionModel selModel;
2023
2024 selModel = selectionModel;
2025 selModel.setValueIsAdjusting(true);
2026 oldLead = getAdjustedIndex(selModel.getLeadSelectionIndex(), true);
2027 oldAnchor = getAdjustedIndex(selModel.getAnchorSelectionIndex(), true);
2028
2029 setRowSelectionInterval(0, getRowCount()-1);
2030
2031 // this is done to restore the anchor and lead
2032 SwingUtilities2.setLeadAnchorWithoutSelection(selModel, oldLead, oldAnchor);
2033
2034 selModel.setValueIsAdjusting(false);
2035
2036 selModel = columnModel.getSelectionModel();
2037 selModel.setValueIsAdjusting(true);
2038 oldLead = getAdjustedIndex(selModel.getLeadSelectionIndex(), false);
2039 oldAnchor = getAdjustedIndex(selModel.getAnchorSelectionIndex(), false);
2040
2041 setColumnSelectionInterval(0, getColumnCount()-1);
2042
2043 // this is done to restore the anchor and lead
2044 SwingUtilities2.setLeadAnchorWithoutSelection(selModel, oldLead, oldAnchor);
2045
2046 selModel.setValueIsAdjusting(false);
2047 }
2048 }
2049
2050 /**
2051 * Deselects all selected columns and rows.
2052 */
2053 public void clearSelection() {
2054 selectionModel.clearSelection();
2055 columnModel.getSelectionModel().clearSelection();
2056 }
2057
2058 private void clearSelectionAndLeadAnchor() {
2059 selectionModel.setValueIsAdjusting(true);
2060 columnModel.getSelectionModel().setValueIsAdjusting(true);
2061
2062 clearSelection();
2063
2064 selectionModel.setAnchorSelectionIndex(-1);
2065 selectionModel.setLeadSelectionIndex(-1);
2066 columnModel.getSelectionModel().setAnchorSelectionIndex(-1);
2067 columnModel.getSelectionModel().setLeadSelectionIndex(-1);
2068
2069 selectionModel.setValueIsAdjusting(false);
2070 columnModel.getSelectionModel().setValueIsAdjusting(false);
2071 }
2072
2073 private int getAdjustedIndex(int index, boolean row) {
2074 int compare = row ? getRowCount() : getColumnCount();
2075 return index < compare ? index : -1;
2076 }
2077
2078 private int boundRow(int row) throws IllegalArgumentException {
2079 if (row < 0 || row >= getRowCount()) {
2080 throw new IllegalArgumentException("Row index out of range");
2081 }
2082 return row;
2083 }
2084
2085 private int boundColumn(int col) {
2086 if (col< 0 || col >= getColumnCount()) {
2087 throw new IllegalArgumentException("Column index out of range");
2088 }
2089 return col;
2090 }
2091
2092 /**
2093 * Selects the rows from <code>index0</code> to <code>index1</code>,
2094 * inclusive.
2095 *
2096 * @exception IllegalArgumentException if <code>index0</code> or
2097 * <code>index1</code> lie outside
2098 * [0, <code>getRowCount()</code>-1]
2099 * @param index0 one end of the interval
2100 * @param index1 the other end of the interval
2101 */
2102 public void setRowSelectionInterval(int index0, int index1) {
2103 selectionModel.setSelectionInterval(boundRow(index0), boundRow(index1));
2104 }
2105
2106 /**
2107 * Selects the columns from <code>index0</code> to <code>index1</code>,
2108 * inclusive.
2109 *
2110 * @exception IllegalArgumentException if <code>index0</code> or
2111 * <code>index1</code> lie outside
2112 * [0, <code>getColumnCount()</code>-1]
2113 * @param index0 one end of the interval
2114 * @param index1 the other end of the interval
2115 */
2116 public void setColumnSelectionInterval(int index0, int index1) {
2117 columnModel.getSelectionModel().setSelectionInterval(boundColumn(index0), boundColumn(index1));
2118 }
2119
2120 /**
2121 * Adds the rows from <code>index0</code> to <code>index1</code>, inclusive, to
2122 * the current selection.
2123 *
2124 * @exception IllegalArgumentException if <code>index0</code> or <code>index1</code>
2125 * lie outside [0, <code>getRowCount()</code>-1]
2126 * @param index0 one end of the interval
2127 * @param index1 the other end of the interval
2128 */
2129 public void addRowSelectionInterval(int index0, int index1) {
2130 selectionModel.addSelectionInterval(boundRow(index0), boundRow(index1));
2131 }
2132
2133 /**
2134 * Adds the columns from <code>index0</code> to <code>index1</code>,
2135 * inclusive, to the current selection.
2136 *
2137 * @exception IllegalArgumentException if <code>index0</code> or
2138 * <code>index1</code> lie outside
2139 * [0, <code>getColumnCount()</code>-1]
2140 * @param index0 one end of the interval
2141 * @param index1 the other end of the interval
2142 */
2143 public void addColumnSelectionInterval(int index0, int index1) {
2144 columnModel.getSelectionModel().addSelectionInterval(boundColumn(index0), boundColumn(index1));
2145 }
2146
2147 /**
2148 * Deselects the rows from <code>index0</code> to <code>index1</code>, inclusive.
2149 *
2150 * @exception IllegalArgumentException if <code>index0</code> or
2151 * <code>index1</code> lie outside
2152 * [0, <code>getRowCount()</code>-1]
2153 * @param index0 one end of the interval
2154 * @param index1 the other end of the interval
2155 */
2156 public void removeRowSelectionInterval(int index0, int index1) {
2157 selectionModel.removeSelectionInterval(boundRow(index0), boundRow(index1));
2158 }
2159
2160 /**
2161 * Deselects the columns from <code>index0</code> to <code>index1</code>, inclusive.
2162 *
2163 * @exception IllegalArgumentException if <code>index0</code> or
2164 * <code>index1</code> lie outside
2165 * [0, <code>getColumnCount()</code>-1]
2166 * @param index0 one end of the interval
2167 * @param index1 the other end of the interval
2168 */
2169 public void removeColumnSelectionInterval(int index0, int index1) {
2170 columnModel.getSelectionModel().removeSelectionInterval(boundColumn(index0), boundColumn(index1));
2171 }
2172
2173 /**
2174 * Returns the index of the first selected row, -1 if no row is selected.
2175 * @return the index of the first selected row
2176 */
2177 public int getSelectedRow() {
2178 return selectionModel.getMinSelectionIndex();
2179 }
2180
2181 /**
2182 * Returns the index of the first selected column,
2183 * -1 if no column is selected.
2184 * @return the index of the first selected column
2185 */
2186 public int getSelectedColumn() {
2187 return columnModel.getSelectionModel().getMinSelectionIndex();
2188 }
2189
2190 /**
2191 * Returns the indices of all selected rows.
2192 *
2193 * @return an array of integers containing the indices of all selected rows,
2194 * or an empty array if no row is selected
2195 * @see #getSelectedRow
2196 */
2197 public int[] getSelectedRows() {
2198 int iMin = selectionModel.getMinSelectionIndex();
2199 int iMax = selectionModel.getMaxSelectionIndex();
2200
2201 if ((iMin == -1) || (iMax == -1)) {
2202 return new int[0];
2203 }
2204
2205 int[] rvTmp = new int[1+ (iMax - iMin)];
2206 int n = 0;
2207 for(int i = iMin; i <= iMax; i++) {
2208 if (selectionModel.isSelectedIndex(i)) {
2209 rvTmp[n++] = i;
2210 }
2211 }
2212 int[] rv = new int[n];
2213 System.arraycopy(rvTmp, 0, rv, 0, n);
2214 return rv;
2215 }
2216
2217 /**
2218 * Returns the indices of all selected columns.
2219 *
2220 * @return an array of integers containing the indices of all selected columns,
2221 * or an empty array if no column is selected
2222 * @see #getSelectedColumn
2223 */
2224 public int[] getSelectedColumns() {
2225 return columnModel.getSelectedColumns();
2226 }
2227
2228 /**
2229 * Returns the number of selected rows.
2230 *
2231 * @return the number of selected rows, 0 if no rows are selected
2232 */
2233 public int getSelectedRowCount() {
2234 int iMin = selectionModel.getMinSelectionIndex();
2235 int iMax = selectionModel.getMaxSelectionIndex();
2236 int count = 0;
2237
2238 for(int i = iMin; i <= iMax; i++) {
2239 if (selectionModel.isSelectedIndex(i)) {
2240 count++;
2241 }
2242 }
2243 return count;
2244 }
2245
2246 /**
2247 * Returns the number of selected columns.
2248 *
2249 * @return the number of selected columns, 0 if no columns are selected
2250 */
2251 public int getSelectedColumnCount() {
2252 return columnModel.getSelectedColumnCount();
2253 }
2254
2255 /**
2256 * Returns true if the specified index is in the valid range of rows,
2257 * and the row at that index is selected.
2258 *
2259 * @return true if <code>row</code> is a valid index and the row at
2260 * that index is selected (where 0 is the first row)
2261 */
2262 public boolean isRowSelected(int row) {
2263 return selectionModel.isSelectedIndex(row);
2264 }
2265
2266 /**
2267 * Returns true if the specified index is in the valid range of columns,
2268 * and the column at that index is selected.
2269 *
2270 * @param column the column in the column model
2271 * @return true if <code>column</code> is a valid index and the column at
2272 * that index is selected (where 0 is the first column)
2273 */
2274 public boolean isColumnSelected(int column) {
2275 return columnModel.getSelectionModel().isSelectedIndex(column);
2276 }
2277
2278 /**
2279 * Returns true if the specified indices are in the valid range of rows
2280 * and columns and the cell at the specified position is selected.
2281 * @param row the row being queried
2282 * @param column the column being queried
2283 *
2284 * @return true if <code>row</code> and <code>column</code> are valid indices
2285 * and the cell at index <code>(row, column)</code> is selected,
2286 * where the first row and first column are at index 0
2287 */
2288 public boolean isCellSelected(int row, int column) {
2289 if (!getRowSelectionAllowed() && !getColumnSelectionAllowed()) {
2290 return false;
2291 }
2292 return (!getRowSelectionAllowed() || isRowSelected(row)) &&
2293 (!getColumnSelectionAllowed() || isColumnSelected(column));
2294 }
2295
2296 private void changeSelectionModel(ListSelectionModel sm, int index,
2297 boolean toggle, boolean extend, boolean selected,
2298 int anchor, boolean anchorSelected) {
2299 if (extend) {
2300 if (toggle) {
2301 if (anchorSelected) {
2302 sm.addSelectionInterval(anchor, index);
2303