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