blob: 585e01dc26b3a80205c1179f3f0cbf57b8a8b7cc [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package javax.swing.plaf.basic;
27
28import java.awt.*;
29import java.awt.datatransfer.*;
30import java.awt.dnd.*;
31import java.awt.event.*;
32import java.util.Enumeration;
33import java.util.EventObject;
34import java.util.Hashtable;
35import java.util.TooManyListenersException;
36import javax.swing.*;
37import javax.swing.event.*;
38import javax.swing.plaf.*;
39import javax.swing.text.*;
40import javax.swing.table.*;
41import javax.swing.plaf.basic.DragRecognitionSupport.BeforeDrag;
42import sun.swing.SwingUtilities2;
43
44
45import java.beans.PropertyChangeEvent;
46import java.beans.PropertyChangeListener;
47
48import sun.swing.DefaultLookup;
49import sun.swing.UIAction;
50
51/**
52 * BasicTableUI implementation
53 *
54 * @author Philip Milne
55 * @author Shannon Hickey (drag and drop)
56 */
57public class BasicTableUI extends TableUI
58{
59 private static final StringBuilder BASELINE_COMPONENT_KEY =
60 new StringBuilder("Table.baselineComponent");
61
62//
63// Instance Variables
64//
65
66 // The JTable that is delegating the painting to this UI.
67 protected JTable table;
68 protected CellRendererPane rendererPane;
69
70 // Listeners that are attached to the JTable
71 protected KeyListener keyListener;
72 protected FocusListener focusListener;
73 protected MouseInputListener mouseInputListener;
74
75 private Handler handler;
76
77 /**
78 * Local cache of Table's client property "Table.isFileList"
79 */
80 private boolean isFileList = false;
81
82//
83// Helper class for keyboard actions
84//
85
86 private static class Actions extends UIAction {
87 private static final String CANCEL_EDITING = "cancel";
88 private static final String SELECT_ALL = "selectAll";
89 private static final String CLEAR_SELECTION = "clearSelection";
90 private static final String START_EDITING = "startEditing";
91
92 private static final String NEXT_ROW = "selectNextRow";
93 private static final String NEXT_ROW_CELL = "selectNextRowCell";
94 private static final String NEXT_ROW_EXTEND_SELECTION =
95 "selectNextRowExtendSelection";
96 private static final String NEXT_ROW_CHANGE_LEAD =
97 "selectNextRowChangeLead";
98 private static final String PREVIOUS_ROW = "selectPreviousRow";
99 private static final String PREVIOUS_ROW_CELL = "selectPreviousRowCell";
100 private static final String PREVIOUS_ROW_EXTEND_SELECTION =
101 "selectPreviousRowExtendSelection";
102 private static final String PREVIOUS_ROW_CHANGE_LEAD =
103 "selectPreviousRowChangeLead";
104
105 private static final String NEXT_COLUMN = "selectNextColumn";
106 private static final String NEXT_COLUMN_CELL = "selectNextColumnCell";
107 private static final String NEXT_COLUMN_EXTEND_SELECTION =
108 "selectNextColumnExtendSelection";
109 private static final String NEXT_COLUMN_CHANGE_LEAD =
110 "selectNextColumnChangeLead";
111 private static final String PREVIOUS_COLUMN = "selectPreviousColumn";
112 private static final String PREVIOUS_COLUMN_CELL =
113 "selectPreviousColumnCell";
114 private static final String PREVIOUS_COLUMN_EXTEND_SELECTION =
115 "selectPreviousColumnExtendSelection";
116 private static final String PREVIOUS_COLUMN_CHANGE_LEAD =
117 "selectPreviousColumnChangeLead";
118
119 private static final String SCROLL_LEFT_CHANGE_SELECTION =
120 "scrollLeftChangeSelection";
121 private static final String SCROLL_LEFT_EXTEND_SELECTION =
122 "scrollLeftExtendSelection";
123 private static final String SCROLL_RIGHT_CHANGE_SELECTION =
124 "scrollRightChangeSelection";
125 private static final String SCROLL_RIGHT_EXTEND_SELECTION =
126 "scrollRightExtendSelection";
127
128 private static final String SCROLL_UP_CHANGE_SELECTION =
129 "scrollUpChangeSelection";
130 private static final String SCROLL_UP_EXTEND_SELECTION =
131 "scrollUpExtendSelection";
132 private static final String SCROLL_DOWN_CHANGE_SELECTION =
133 "scrollDownChangeSelection";
134 private static final String SCROLL_DOWN_EXTEND_SELECTION =
135 "scrollDownExtendSelection";
136
137 private static final String FIRST_COLUMN =
138 "selectFirstColumn";
139 private static final String FIRST_COLUMN_EXTEND_SELECTION =
140 "selectFirstColumnExtendSelection";
141 private static final String LAST_COLUMN =
142 "selectLastColumn";
143 private static final String LAST_COLUMN_EXTEND_SELECTION =
144 "selectLastColumnExtendSelection";
145
146 private static final String FIRST_ROW =
147 "selectFirstRow";
148 private static final String FIRST_ROW_EXTEND_SELECTION =
149 "selectFirstRowExtendSelection";
150 private static final String LAST_ROW =
151 "selectLastRow";
152 private static final String LAST_ROW_EXTEND_SELECTION =
153 "selectLastRowExtendSelection";
154
155 // add the lead item to the selection without changing lead or anchor
156 private static final String ADD_TO_SELECTION = "addToSelection";
157
158 // toggle the selected state of the lead item and move the anchor to it
159 private static final String TOGGLE_AND_ANCHOR = "toggleAndAnchor";
160
161 // extend the selection to the lead item
162 private static final String EXTEND_TO = "extendTo";
163
164 // move the anchor to the lead and ensure only that item is selected
165 private static final String MOVE_SELECTION_TO = "moveSelectionTo";
166
167 // give focus to the JTableHeader, if one exists
168 private static final String FOCUS_HEADER = "focusHeader";
169
170 protected int dx;
171 protected int dy;
172 protected boolean extend;
173 protected boolean inSelection;
174
175 // horizontally, forwards always means right,
176 // regardless of component orientation
177 protected boolean forwards;
178 protected boolean vertically;
179 protected boolean toLimit;
180
181 protected int leadRow;
182 protected int leadColumn;
183
184 Actions(String name) {
185 super(name);
186 }
187
188 Actions(String name, int dx, int dy, boolean extend,
189 boolean inSelection) {
190 super(name);
191
192 // Actions spcifying true for "inSelection" are
193 // fairly sensitive to bad parameter values. They require
194 // that one of dx and dy be 0 and the other be -1 or 1.
195 // Bogus parameter values could cause an infinite loop.
196 // To prevent any problems we massage the params here
197 // and complain if we get something we can't deal with.
198 if (inSelection) {
199 this.inSelection = true;
200
201 // look at the sign of dx and dy only
202 dx = sign(dx);
203 dy = sign(dy);
204
205 // make sure one is zero, but not both
206 assert (dx == 0 || dy == 0) && !(dx == 0 && dy == 0);
207 }
208
209 this.dx = dx;
210 this.dy = dy;
211 this.extend = extend;
212 }
213
214 Actions(String name, boolean extend, boolean forwards,
215 boolean vertically, boolean toLimit) {
216 this(name, 0, 0, extend, false);
217 this.forwards = forwards;
218 this.vertically = vertically;
219 this.toLimit = toLimit;
220 }
221
222 private static int clipToRange(int i, int a, int b) {
223 return Math.min(Math.max(i, a), b-1);
224 }
225
226 private void moveWithinTableRange(JTable table, int dx, int dy) {
227 leadRow = clipToRange(leadRow+dy, 0, table.getRowCount());
228 leadColumn = clipToRange(leadColumn+dx, 0, table.getColumnCount());
229 }
230
231 private static int sign(int num) {
232 return (num < 0) ? -1 : ((num == 0) ? 0 : 1);
233 }
234
235 /**
236 * Called to move within the selected range of the given JTable.
237 * This method uses the table's notion of selection, which is
238 * important to allow the user to navigate between items visually
239 * selected on screen. This notion may or may not be the same as
240 * what could be determined by directly querying the selection models.
241 * It depends on certain table properties (such as whether or not
242 * row or column selection is allowed). When performing modifications,
243 * it is recommended that caution be taken in order to preserve
244 * the intent of this method, especially when deciding whether to
245 * query the selection models or interact with JTable directly.
246 */
247 private boolean moveWithinSelectedRange(JTable table, int dx, int dy,
248 ListSelectionModel rsm, ListSelectionModel csm) {
249
250 // Note: The Actions constructor ensures that only one of
251 // dx and dy is 0, and the other is either -1 or 1
252
253 // find out how many items the table is showing as selected
254 // and the range of items to navigate through
255 int totalCount;
256 int minX, maxX, minY, maxY;
257
258 boolean rs = table.getRowSelectionAllowed();
259 boolean cs = table.getColumnSelectionAllowed();
260
261 // both column and row selection
262 if (rs && cs) {
263 totalCount = table.getSelectedRowCount() * table.getSelectedColumnCount();
264 minX = csm.getMinSelectionIndex();
265 maxX = csm.getMaxSelectionIndex();
266 minY = rsm.getMinSelectionIndex();
267 maxY = rsm.getMaxSelectionIndex();
268 // row selection only
269 } else if (rs) {
270 totalCount = table.getSelectedRowCount();
271 minX = 0;
272 maxX = table.getColumnCount() - 1;
273 minY = rsm.getMinSelectionIndex();
274 maxY = rsm.getMaxSelectionIndex();
275 // column selection only
276 } else if (cs) {
277 totalCount = table.getSelectedColumnCount();
278 minX = csm.getMinSelectionIndex();
279 maxX = csm.getMaxSelectionIndex();
280 minY = 0;
281 maxY = table.getRowCount() - 1;
282 // no selection allowed
283 } else {
284 totalCount = 0;
285 // A bogus assignment to stop javac from complaining
286 // about unitialized values. In this case, these
287 // won't even be used.
288 minX = maxX = minY = maxY = 0;
289 }
290
291 // For some cases, there is no point in trying to stay within the
292 // selected area. Instead, move outside the selection, wrapping at
293 // the table boundaries. The cases are:
294 boolean stayInSelection;
295
296 // - nothing selected
297 if (totalCount == 0 ||
298 // - one item selected, and the lead is already selected
299 (totalCount == 1 && table.isCellSelected(leadRow, leadColumn))) {
300
301 stayInSelection = false;
302
303 maxX = table.getColumnCount() - 1;
304 maxY = table.getRowCount() - 1;
305
306 // the mins are calculated like this in case the max is -1
307 minX = Math.min(0, maxX);
308 minY = Math.min(0, maxY);
309 } else {
310 stayInSelection = true;
311 }
312
313 // the algorithm below isn't prepared to deal with -1 lead/anchor
314 // so massage appropriately here first
315 if (dy == 1 && leadColumn == -1) {
316 leadColumn = minX;
317 leadRow = -1;
318 } else if (dx == 1 && leadRow == -1) {
319 leadRow = minY;
320 leadColumn = -1;
321 } else if (dy == -1 && leadColumn == -1) {
322 leadColumn = maxX;
323 leadRow = maxY + 1;
324 } else if (dx == -1 && leadRow == -1) {
325 leadRow = maxY;
326 leadColumn = maxX + 1;
327 }
328
329 // In cases where the lead is not within the search range,
330 // we need to bring it within one cell for the the search
331 // to work properly. Check these here.
332 leadRow = Math.min(Math.max(leadRow, minY - 1), maxY + 1);
333 leadColumn = Math.min(Math.max(leadColumn, minX - 1), maxX + 1);
334
335 // find the next position, possibly looping until it is selected
336 do {
337 calcNextPos(dx, minX, maxX, dy, minY, maxY);
338 } while (stayInSelection && !table.isCellSelected(leadRow, leadColumn));
339
340 return stayInSelection;
341 }
342
343 /**
344 * Find the next lead row and column based on the given
345 * dx/dy and max/min values.
346 */
347 private void calcNextPos(int dx, int minX, int maxX,
348 int dy, int minY, int maxY) {
349
350 if (dx != 0) {
351 leadColumn += dx;
352 if (leadColumn > maxX) {
353 leadColumn = minX;
354 leadRow++;
355 if (leadRow > maxY) {
356 leadRow = minY;
357 }
358 } else if (leadColumn < minX) {
359 leadColumn = maxX;
360 leadRow--;
361 if (leadRow < minY) {
362 leadRow = maxY;
363 }
364 }
365 } else {
366 leadRow += dy;
367 if (leadRow > maxY) {
368 leadRow = minY;
369 leadColumn++;
370 if (leadColumn > maxX) {
371 leadColumn = minX;
372 }
373 } else if (leadRow < minY) {
374 leadRow = maxY;
375 leadColumn--;
376 if (leadColumn < minX) {
377 leadColumn = maxX;
378 }
379 }
380 }
381 }
382
383 public void actionPerformed(ActionEvent e) {
384 String key = getName();
385 JTable table = (JTable)e.getSource();
386
387 ListSelectionModel rsm = table.getSelectionModel();
388 leadRow = getAdjustedLead(table, true, rsm);
389
390 ListSelectionModel csm = table.getColumnModel().getSelectionModel();
391 leadColumn = getAdjustedLead(table, false, csm);
392
393 if (key == SCROLL_LEFT_CHANGE_SELECTION || // Paging Actions
394 key == SCROLL_LEFT_EXTEND_SELECTION ||
395 key == SCROLL_RIGHT_CHANGE_SELECTION ||
396 key == SCROLL_RIGHT_EXTEND_SELECTION ||
397 key == SCROLL_UP_CHANGE_SELECTION ||
398 key == SCROLL_UP_EXTEND_SELECTION ||
399 key == SCROLL_DOWN_CHANGE_SELECTION ||
400 key == SCROLL_DOWN_EXTEND_SELECTION ||
401 key == FIRST_COLUMN ||
402 key == FIRST_COLUMN_EXTEND_SELECTION ||
403 key == FIRST_ROW ||
404 key == FIRST_ROW_EXTEND_SELECTION ||
405 key == LAST_COLUMN ||
406 key == LAST_COLUMN_EXTEND_SELECTION ||
407 key == LAST_ROW ||
408 key == LAST_ROW_EXTEND_SELECTION) {
409 if (toLimit) {
410 if (vertically) {
411 int rowCount = table.getRowCount();
412 this.dx = 0;
413 this.dy = forwards ? rowCount : -rowCount;
414 }
415 else {
416 int colCount = table.getColumnCount();
417 this.dx = forwards ? colCount : -colCount;
418 this.dy = 0;
419 }
420 }
421 else {
422 if (!(table.getParent().getParent() instanceof
423 JScrollPane)) {
424 return;
425 }
426
427 Dimension delta = table.getParent().getSize();
428
429 if (vertically) {
430 Rectangle r = table.getCellRect(leadRow, 0, true);
431 if (forwards) {
432 // scroll by at least one cell
433 r.y += Math.max(delta.height, r.height);
434 } else {
435 r.y -= delta.height;
436 }
437
438 this.dx = 0;
439 int newRow = table.rowAtPoint(r.getLocation());
440 if (newRow == -1 && forwards) {
441 newRow = table.getRowCount();
442 }
443 this.dy = newRow - leadRow;
444 }
445 else {
446 Rectangle r = table.getCellRect(0, leadColumn, true);
447
448 if (forwards) {
449 // scroll by at least one cell
450 r.x += Math.max(delta.width, r.width);
451 } else {
452 r.x -= delta.width;
453 }
454
455 int newColumn = table.columnAtPoint(r.getLocation());
456 if (newColumn == -1) {
457 boolean ltr = table.getComponentOrientation().isLeftToRight();
458
459 newColumn = forwards ? (ltr ? table.getColumnCount() : 0)
460 : (ltr ? 0 : table.getColumnCount());
461
462 }
463 this.dx = newColumn - leadColumn;
464 this.dy = 0;
465 }
466 }
467 }
468 if (key == NEXT_ROW || // Navigate Actions
469 key == NEXT_ROW_CELL ||
470 key == NEXT_ROW_EXTEND_SELECTION ||
471 key == NEXT_ROW_CHANGE_LEAD ||
472 key == NEXT_COLUMN ||
473 key == NEXT_COLUMN_CELL ||
474 key == NEXT_COLUMN_EXTEND_SELECTION ||
475 key == NEXT_COLUMN_CHANGE_LEAD ||
476 key == PREVIOUS_ROW ||
477 key == PREVIOUS_ROW_CELL ||
478 key == PREVIOUS_ROW_EXTEND_SELECTION ||
479 key == PREVIOUS_ROW_CHANGE_LEAD ||
480 key == PREVIOUS_COLUMN ||
481 key == PREVIOUS_COLUMN_CELL ||
482 key == PREVIOUS_COLUMN_EXTEND_SELECTION ||
483 key == PREVIOUS_COLUMN_CHANGE_LEAD ||
484 // Paging Actions.
485 key == SCROLL_LEFT_CHANGE_SELECTION ||
486 key == SCROLL_LEFT_EXTEND_SELECTION ||
487 key == SCROLL_RIGHT_CHANGE_SELECTION ||
488 key == SCROLL_RIGHT_EXTEND_SELECTION ||
489 key == SCROLL_UP_CHANGE_SELECTION ||
490 key == SCROLL_UP_EXTEND_SELECTION ||
491 key == SCROLL_DOWN_CHANGE_SELECTION ||
492 key == SCROLL_DOWN_EXTEND_SELECTION ||
493 key == FIRST_COLUMN ||
494 key == FIRST_COLUMN_EXTEND_SELECTION ||
495 key == FIRST_ROW ||
496 key == FIRST_ROW_EXTEND_SELECTION ||
497 key == LAST_COLUMN ||
498 key == LAST_COLUMN_EXTEND_SELECTION ||
499 key == LAST_ROW ||
500 key == LAST_ROW_EXTEND_SELECTION) {
501
502 if (table.isEditing() &&
503 !table.getCellEditor().stopCellEditing()) {
504 return;
505 }
506
507 // Unfortunately, this strategy introduces bugs because
508 // of the asynchronous nature of requestFocus() call below.
509 // Introducing a delay with invokeLater() makes this work
510 // in the typical case though race conditions then allow
511 // focus to disappear altogether. The right solution appears
512 // to be to fix requestFocus() so that it queues a request
513 // for the focus regardless of who owns the focus at the
514 // time the call to requestFocus() is made. The optimisation
515 // to ignore the call to requestFocus() when the component
516 // already has focus may ligitimately be made as the
517 // request focus event is dequeued, not before.
518
519 // boolean wasEditingWithFocus = table.isEditing() &&
520 // table.getEditorComponent().isFocusOwner();
521
522 boolean changeLead = false;
523 if (key == NEXT_ROW_CHANGE_LEAD || key == PREVIOUS_ROW_CHANGE_LEAD) {
524 changeLead = (rsm.getSelectionMode()
525 == ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
526 } else if (key == NEXT_COLUMN_CHANGE_LEAD || key == PREVIOUS_COLUMN_CHANGE_LEAD) {
527 changeLead = (csm.getSelectionMode()
528 == ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
529 }
530
531 if (changeLead) {
532 moveWithinTableRange(table, dx, dy);
533 if (dy != 0) {
534 // casting should be safe since the action is only enabled
535 // for DefaultListSelectionModel
536 ((DefaultListSelectionModel)rsm).moveLeadSelectionIndex(leadRow);
537 if (getAdjustedLead(table, false, csm) == -1
538 && table.getColumnCount() > 0) {
539
540 ((DefaultListSelectionModel)csm).moveLeadSelectionIndex(0);
541 }
542 } else {
543 // casting should be safe since the action is only enabled
544 // for DefaultListSelectionModel
545 ((DefaultListSelectionModel)csm).moveLeadSelectionIndex(leadColumn);
546 if (getAdjustedLead(table, true, rsm) == -1
547 && table.getRowCount() > 0) {
548
549 ((DefaultListSelectionModel)rsm).moveLeadSelectionIndex(0);
550 }
551 }
552
553 Rectangle cellRect = table.getCellRect(leadRow, leadColumn, false);
554 if (cellRect != null) {
555 table.scrollRectToVisible(cellRect);
556 }
557 } else if (!inSelection) {
558 moveWithinTableRange(table, dx, dy);
559 table.changeSelection(leadRow, leadColumn, false, extend);
560 }
561 else {
562 if (table.getRowCount() <= 0 || table.getColumnCount() <= 0) {
563 // bail - don't try to move selection on an empty table
564 return;
565 }
566
567 if (moveWithinSelectedRange(table, dx, dy, rsm, csm)) {
568 // this is the only way we have to set both the lead
569 // and the anchor without changing the selection
570 if (rsm.isSelectedIndex(leadRow)) {
571 rsm.addSelectionInterval(leadRow, leadRow);
572 } else {
573 rsm.removeSelectionInterval(leadRow, leadRow);
574 }
575
576 if (csm.isSelectedIndex(leadColumn)) {
577 csm.addSelectionInterval(leadColumn, leadColumn);
578 } else {
579 csm.removeSelectionInterval(leadColumn, leadColumn);
580 }
581
582 Rectangle cellRect = table.getCellRect(leadRow, leadColumn, false);
583 if (cellRect != null) {
584 table.scrollRectToVisible(cellRect);
585 }
586 }
587 else {
588 table.changeSelection(leadRow, leadColumn,
589 false, false);
590 }
591 }
592
593 /*
594 if (wasEditingWithFocus) {
595 table.editCellAt(leadRow, leadColumn);
596 final Component editorComp = table.getEditorComponent();
597 if (editorComp != null) {
598 SwingUtilities.invokeLater(new Runnable() {
599 public void run() {
600 editorComp.requestFocus();
601 }
602 });
603 }
604 }
605 */
606 } else if (key == CANCEL_EDITING) {
607 table.removeEditor();
608 } else if (key == SELECT_ALL) {
609 table.selectAll();
610 } else if (key == CLEAR_SELECTION) {
611 table.clearSelection();
612 } else if (key == START_EDITING) {
613 if (!table.hasFocus()) {
614 CellEditor cellEditor = table.getCellEditor();
615 if (cellEditor != null && !cellEditor.stopCellEditing()) {
616 return;
617 }
618 table.requestFocus();
619 return;
620 }
621 table.editCellAt(leadRow, leadColumn, e);
622 Component editorComp = table.getEditorComponent();
623 if (editorComp != null) {
624 editorComp.requestFocus();
625 }
626 } else if (key == ADD_TO_SELECTION) {
627 if (!table.isCellSelected(leadRow, leadColumn)) {
628 int oldAnchorRow = rsm.getAnchorSelectionIndex();
629 int oldAnchorColumn = csm.getAnchorSelectionIndex();
630 rsm.setValueIsAdjusting(true);
631 csm.setValueIsAdjusting(true);
632 table.changeSelection(leadRow, leadColumn, true, false);
633 rsm.setAnchorSelectionIndex(oldAnchorRow);
634 csm.setAnchorSelectionIndex(oldAnchorColumn);
635 rsm.setValueIsAdjusting(false);
636 csm.setValueIsAdjusting(false);
637 }
638 } else if (key == TOGGLE_AND_ANCHOR) {
639 table.changeSelection(leadRow, leadColumn, true, false);
640 } else if (key == EXTEND_TO) {
641 table.changeSelection(leadRow, leadColumn, false, true);
642 } else if (key == MOVE_SELECTION_TO) {
643 table.changeSelection(leadRow, leadColumn, false, false);
644 } else if (key == FOCUS_HEADER) {
645 JTableHeader th = table.getTableHeader();
646 if (th != null) {
647 //Set the header's selected column to match the table.
648 int col = table.getSelectedColumn();
649 if (col >= 0) {
650 TableHeaderUI thUI = th.getUI();
651 if (thUI instanceof BasicTableHeaderUI) {
652 ((BasicTableHeaderUI)thUI).selectColumn(col);
653 }
654 }
655
656 //Then give the header the focus.
657 th.requestFocusInWindow();
658 }
659 }
660 }
661
662 public boolean isEnabled(Object sender) {
663 String key = getName();
664
665 if (sender instanceof JTable &&
666 Boolean.TRUE.equals(((JTable)sender).getClientProperty("Table.isFileList"))) {
667 if (key == NEXT_COLUMN ||
668 key == NEXT_COLUMN_CELL ||
669 key == NEXT_COLUMN_EXTEND_SELECTION ||
670 key == NEXT_COLUMN_CHANGE_LEAD ||
671 key == PREVIOUS_COLUMN ||
672 key == PREVIOUS_COLUMN_CELL ||
673 key == PREVIOUS_COLUMN_EXTEND_SELECTION ||
674 key == PREVIOUS_COLUMN_CHANGE_LEAD ||
675 key == SCROLL_LEFT_CHANGE_SELECTION ||
676 key == SCROLL_LEFT_EXTEND_SELECTION ||
677 key == SCROLL_RIGHT_CHANGE_SELECTION ||
678 key == SCROLL_RIGHT_EXTEND_SELECTION ||
679 key == FIRST_COLUMN ||
680 key == FIRST_COLUMN_EXTEND_SELECTION ||
681 key == LAST_COLUMN ||
682 key == LAST_COLUMN_EXTEND_SELECTION ||
683 key == NEXT_ROW_CELL ||
684 key == PREVIOUS_ROW_CELL) {
685
686 return false;
687 }
688 }
689
690 if (key == CANCEL_EDITING && sender instanceof JTable) {
691 return ((JTable)sender).isEditing();
692 } else if (key == NEXT_ROW_CHANGE_LEAD ||
693 key == PREVIOUS_ROW_CHANGE_LEAD) {
694 // discontinuous selection actions are only enabled for
695 // DefaultListSelectionModel
696 return sender != null &&
697 ((JTable)sender).getSelectionModel()
698 instanceof DefaultListSelectionModel;
699 } else if (key == NEXT_COLUMN_CHANGE_LEAD ||
700 key == PREVIOUS_COLUMN_CHANGE_LEAD) {
701 // discontinuous selection actions are only enabled for
702 // DefaultListSelectionModel
703 return sender != null &&
704 ((JTable)sender).getColumnModel().getSelectionModel()
705 instanceof DefaultListSelectionModel;
706 } else if (key == ADD_TO_SELECTION && sender instanceof JTable) {
707 // This action is typically bound to SPACE.
708 // If the table is already in an editing mode, SPACE should
709 // simply enter a space character into the table, and not
710 // select a cell. Likewise, if the lead cell is already selected
711 // then hitting SPACE should just enter a space character
712 // into the cell and begin editing. In both of these cases
713 // this action will be disabled.
714 JTable table = (JTable)sender;
715 int leadRow = getAdjustedLead(table, true);
716 int leadCol = getAdjustedLead(table, false);
717 return !(table.isEditing() || table.isCellSelected(leadRow, leadCol));
718 } else if (key == FOCUS_HEADER && sender instanceof JTable) {
719 JTable table = (JTable)sender;
720 return table.getTableHeader() != null;
721 }
722
723 return true;
724 }
725 }
726
727
728//
729// The Table's Key listener
730//
731
732 /**
733 * This inner class is marked &quot;public&quot; due to a compiler bug.
734 * This class should be treated as a &quot;protected&quot; inner class.
735 * Instantiate it only within subclasses of BasicTableUI.
736 * <p>As of Java 2 platform v1.3 this class is no longer used.
737 * Instead <code>JTable</code>
738 * overrides <code>processKeyBinding</code> to dispatch the event to
739 * the current <code>TableCellEditor</code>.
740 */
741 public class KeyHandler implements KeyListener {
742 // NOTE: This class exists only for backward compatability. All
743 // its functionality has been moved into Handler. If you need to add
744 // new functionality add it to the Handler, but make sure this
745 // class calls into the Handler.
746 public void keyPressed(KeyEvent e) {
747 getHandler().keyPressed(e);
748 }
749
750 public void keyReleased(KeyEvent e) {
751 getHandler().keyReleased(e);
752 }
753
754 public void keyTyped(KeyEvent e) {
755 getHandler().keyTyped(e);
756 }
757 }
758
759//
760// The Table's focus listener
761//
762
763 /**
764 * This inner class is marked &quot;public&quot; due to a compiler bug.
765 * This class should be treated as a &quot;protected&quot; inner class.
766 * Instantiate it only within subclasses of BasicTableUI.
767 */
768 public class FocusHandler implements FocusListener {
769 // NOTE: This class exists only for backward compatability. All
770 // its functionality has been moved into Handler. If you need to add
771 // new functionality add it to the Handler, but make sure this
772 // class calls into the Handler.
773 public void focusGained(FocusEvent e) {
774 getHandler().focusGained(e);
775 }
776
777 public void focusLost(FocusEvent e) {
778 getHandler().focusLost(e);
779 }
780 }
781
782//
783// The Table's mouse and mouse motion listeners
784//
785
786 /**
787 * This inner class is marked &quot;public&quot; due to a compiler bug.
788 * This class should be treated as a &quot;protected&quot; inner class.
789 * Instantiate it only within subclasses of BasicTableUI.
790 */
791 public class MouseInputHandler implements MouseInputListener {
792 // NOTE: This class exists only for backward compatability. All
793 // its functionality has been moved into Handler. If you need to add
794 // new functionality add it to the Handler, but make sure this
795 // class calls into the Handler.
796 public void mouseClicked(MouseEvent e) {
797 getHandler().mouseClicked(e);
798 }
799
800 public void mousePressed(MouseEvent e) {
801 getHandler().mousePressed(e);
802 }
803
804 public void mouseReleased(MouseEvent e) {
805 getHandler().mouseReleased(e);
806 }
807
808 public void mouseEntered(MouseEvent e) {
809 getHandler().mouseEntered(e);
810 }
811
812 public void mouseExited(MouseEvent e) {
813 getHandler().mouseExited(e);
814 }
815
816 public void mouseMoved(MouseEvent e) {
817 getHandler().mouseMoved(e);
818 }
819
820 public void mouseDragged(MouseEvent e) {
821 getHandler().mouseDragged(e);
822 }
823 }
824
825 private class Handler implements FocusListener, MouseInputListener,
826 PropertyChangeListener, ListSelectionListener, ActionListener,
827 BeforeDrag {
828
829 // FocusListener
830 private void repaintLeadCell( ) {
831 int lr = getAdjustedLead(table, true);
832 int lc = getAdjustedLead(table, false);
833
834 if (lr < 0 || lc < 0) {
835 return;
836 }
837
838 Rectangle dirtyRect = table.getCellRect(lr, lc, false);
839 table.repaint(dirtyRect);
840 }
841
842 public void focusGained(FocusEvent e) {
843 repaintLeadCell();
844 }
845
846 public void focusLost(FocusEvent e) {
847 repaintLeadCell();
848 }
849
850
851 // KeyListener
852 public void keyPressed(KeyEvent e) { }
853
854 public void keyReleased(KeyEvent e) { }
855
856 public void keyTyped(KeyEvent e) {
857 KeyStroke keyStroke = KeyStroke.getKeyStroke(e.getKeyChar(),
858 e.getModifiers());
859
860 // We register all actions using ANCESTOR_OF_FOCUSED_COMPONENT
861 // which means that we might perform the appropriate action
862 // in the table and then forward it to the editor if the editor
863 // had focus. Make sure this doesn't happen by checking our
864 // InputMaps.
865 InputMap map = table.getInputMap(JComponent.WHEN_FOCUSED);
866 if (map != null && map.get(keyStroke) != null) {
867 return;
868 }
869 map = table.getInputMap(JComponent.
870 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
871 if (map != null && map.get(keyStroke) != null) {
872 return;
873 }
874
875 keyStroke = KeyStroke.getKeyStrokeForEvent(e);
876
877 // The AWT seems to generate an unconsumed \r event when
878 // ENTER (\n) is pressed.
879 if (e.getKeyChar() == '\r') {
880 return;
881 }
882
883 int leadRow = getAdjustedLead(table, true);
884 int leadColumn = getAdjustedLead(table, false);
885 if (leadRow != -1 && leadColumn != -1 && !table.isEditing()) {
886 if (!table.editCellAt(leadRow, leadColumn)) {
887 return;
888 }
889 }
890
891 // Forwarding events this way seems to put the component
892 // in a state where it believes it has focus. In reality
893 // the table retains focus - though it is difficult for
894 // a user to tell, since the caret is visible and flashing.
895
896 // Calling table.requestFocus() here, to get the focus back to
897 // the table, seems to have no effect.
898
899 Component editorComp = table.getEditorComponent();
900 if (table.isEditing() && editorComp != null) {
901 if (editorComp instanceof JComponent) {
902 JComponent component = (JComponent)editorComp;
903 map = component.getInputMap(JComponent.WHEN_FOCUSED);
904 Object binding = (map != null) ? map.get(keyStroke) : null;
905 if (binding == null) {
906 map = component.getInputMap(JComponent.
907 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
908 binding = (map != null) ? map.get(keyStroke) : null;
909 }
910 if (binding != null) {
911 ActionMap am = component.getActionMap();
912 Action action = (am != null) ? am.get(binding) : null;
913 if (action != null && SwingUtilities.
914 notifyAction(action, keyStroke, e, component,
915 e.getModifiers())) {
916 e.consume();
917 }
918 }
919 }
920 }
921 }
922
923
924 // MouseInputListener
925
926 // Component receiving mouse events during editing.
927 // May not be editorComponent.
928 private Component dispatchComponent;
929
930 public void mouseClicked(MouseEvent e) {}
931
932 private void setDispatchComponent(MouseEvent e) {
933 Component editorComponent = table.getEditorComponent();
934 Point p = e.getPoint();
935 Point p2 = SwingUtilities.convertPoint(table, p, editorComponent);
936 dispatchComponent =
937 SwingUtilities.getDeepestComponentAt(editorComponent,
938 p2.x, p2.y);
939 SwingUtilities2.setSkipClickCount(dispatchComponent,
940 e.getClickCount() - 1);
941 }
942
943 private boolean repostEvent(MouseEvent e) {
944 // Check for isEditing() in case another event has
945 // caused the editor to be removed. See bug #4306499.
946 if (dispatchComponent == null || !table.isEditing()) {
947 return false;
948 }
949 MouseEvent e2 = SwingUtilities.convertMouseEvent(table, e,
950 dispatchComponent);
951 dispatchComponent.dispatchEvent(e2);
952 return true;
953 }
954
955 private void setValueIsAdjusting(boolean flag) {
956 table.getSelectionModel().setValueIsAdjusting(flag);
957 table.getColumnModel().getSelectionModel().
958 setValueIsAdjusting(flag);
959 }
960
961 // The row and column where the press occurred and the
962 // press event itself
963 private int pressedRow;
964 private int pressedCol;
965 private MouseEvent pressedEvent;
966
967 // Whether or not the mouse press (which is being considered as part
968 // of a drag sequence) also caused the selection change to be fully
969 // processed.
970 private boolean dragPressDidSelection;
971
972 // Set to true when a drag gesture has been fully recognized and DnD
973 // begins. Use this to ignore further mouse events which could be
974 // delivered if DnD is cancelled (via ESCAPE for example)
975 private boolean dragStarted;
976
977 // Whether or not we should start the editing timer on release
978 private boolean shouldStartTimer;
979
980 // To cache the return value of pointOutsidePrefSize since we use
981 // it multiple times.
982 private boolean outsidePrefSize;
983
984 // Used to delay the start of editing.
985 private Timer timer = null;
986
987 private boolean canStartDrag() {
988 if (pressedRow == -1 || pressedCol == -1) {
989 return false;
990 }
991
992 if (isFileList) {
993 return !outsidePrefSize;
994 }
995
996 // if this is a single selection table
997 if ((table.getSelectionModel().getSelectionMode() ==
998 ListSelectionModel.SINGLE_SELECTION) &&
999 (table.getColumnModel().getSelectionModel().getSelectionMode() ==
1000 ListSelectionModel.SINGLE_SELECTION)) {
1001
1002 return true;
1003 }
1004
1005 return table.isCellSelected(pressedRow, pressedCol);
1006 }
1007
1008 public void mousePressed(MouseEvent e) {
1009 if (SwingUtilities2.shouldIgnore(e, table)) {
1010 return;
1011 }
1012
1013 if (table.isEditing() && !table.getCellEditor().stopCellEditing()) {
1014 Component editorComponent = table.getEditorComponent();
1015 if (editorComponent != null && !editorComponent.hasFocus()) {
1016 SwingUtilities2.compositeRequestFocus(editorComponent);
1017 }
1018 return;
1019 }
1020
1021 Point p = e.getPoint();
1022 pressedRow = table.rowAtPoint(p);
1023 pressedCol = table.columnAtPoint(p);
1024 outsidePrefSize = pointOutsidePrefSize(pressedRow, pressedCol, p);
1025
1026 if (isFileList) {
1027 shouldStartTimer =
1028 table.isCellSelected(pressedRow, pressedCol) &&
1029 !e.isShiftDown() &&
1030 !e.isControlDown() &&
1031 !outsidePrefSize;
1032 }
1033
1034 if (table.getDragEnabled()) {
1035 mousePressedDND(e);
1036 } else {
1037 SwingUtilities2.adjustFocus(table);
1038 if (!isFileList) {
1039 setValueIsAdjusting(true);
1040 }
1041 adjustSelection(e);
1042 }
1043 }
1044
1045 private void mousePressedDND(MouseEvent e) {
1046 pressedEvent = e;
1047 boolean grabFocus = true;
1048 dragStarted = false;
1049
1050 if (canStartDrag() && DragRecognitionSupport.mousePressed(e)) {
1051
1052 dragPressDidSelection = false;
1053
1054 if (e.isControlDown() && isFileList) {
1055 // do nothing for control - will be handled on release
1056 // or when drag starts
1057 return;
1058 } else if (!e.isShiftDown() && table.isCellSelected(pressedRow, pressedCol)) {
1059 // clicking on something that's already selected
1060 // and need to make it the lead now
1061 table.getSelectionModel().addSelectionInterval(pressedRow,
1062 pressedRow);
1063 table.getColumnModel().getSelectionModel().
1064 addSelectionInterval(pressedCol, pressedCol);
1065
1066 return;
1067 }
1068
1069 dragPressDidSelection = true;
1070
1071 // could be a drag initiating event - don't grab focus
1072 grabFocus = false;
1073 } else if (!isFileList) {
1074 // When drag can't happen, mouse drags might change the selection in the table
1075 // so we want the isAdjusting flag to be set
1076 setValueIsAdjusting(true);
1077 }
1078
1079 if (grabFocus) {
1080 SwingUtilities2.adjustFocus(table);
1081 }
1082
1083 adjustSelection(e);
1084 }
1085
1086 private void adjustSelection(MouseEvent e) {
1087 // Fix for 4835633
1088 if (outsidePrefSize) {
1089 // If shift is down in multi-select, we should just return.
1090 // For single select or non-shift-click, clear the selection
1091 if (e.getID() == MouseEvent.MOUSE_PRESSED &&
1092 (!e.isShiftDown() ||
1093 table.getSelectionModel().getSelectionMode() ==
1094 ListSelectionModel.SINGLE_SELECTION)) {
1095 table.clearSelection();
1096 TableCellEditor tce = table.getCellEditor();
1097 if (tce != null) {
1098 tce.stopCellEditing();
1099 }
1100 }
1101 return;
1102 }
1103 // The autoscroller can generate drag events outside the
1104 // table's range.
1105 if ((pressedCol == -1) || (pressedRow == -1)) {
1106 return;
1107 }
1108
1109 boolean dragEnabled = table.getDragEnabled();
1110
1111 if (!dragEnabled && !isFileList && table.editCellAt(pressedRow, pressedCol, e)) {
1112 setDispatchComponent(e);
1113 repostEvent(e);
1114 }
1115
1116 CellEditor editor = table.getCellEditor();
1117 if (dragEnabled || editor == null || editor.shouldSelectCell(e)) {
1118 table.changeSelection(pressedRow, pressedCol, e.isControlDown(), e.isShiftDown());
1119 }
1120 }
1121
1122 public void valueChanged(ListSelectionEvent e) {
1123 if (timer != null) {
1124 timer.stop();
1125 timer = null;
1126 }
1127 }
1128
1129 public void actionPerformed(ActionEvent ae) {
1130 table.editCellAt(pressedRow, pressedCol, null);
1131 Component editorComponent = table.getEditorComponent();
1132 if (editorComponent != null && !editorComponent.hasFocus()) {
1133 SwingUtilities2.compositeRequestFocus(editorComponent);
1134 }
1135 return;
1136 }
1137
1138 private void maybeStartTimer() {
1139 if (!shouldStartTimer) {
1140 return;
1141 }
1142
1143 if (timer == null) {
1144 timer = new Timer(1200, this);
1145 timer.setRepeats(false);
1146 }
1147
1148 timer.start();
1149 }
1150
1151 public void mouseReleased(MouseEvent e) {
1152 if (SwingUtilities2.shouldIgnore(e, table)) {
1153 return;
1154 }
1155
1156 if (table.getDragEnabled()) {
1157 mouseReleasedDND(e);
1158 } else {
1159 if (isFileList) {
1160 maybeStartTimer();
1161 }
1162 }
1163
1164 pressedEvent = null;
1165 repostEvent(e);
1166 dispatchComponent = null;
1167 setValueIsAdjusting(false);
1168 }
1169
1170 private void mouseReleasedDND(MouseEvent e) {
1171 MouseEvent me = DragRecognitionSupport.mouseReleased(e);
1172 if (me != null) {
1173 SwingUtilities2.adjustFocus(table);
1174 if (!dragPressDidSelection) {
1175 adjustSelection(me);
1176 }
1177 }
1178
1179 if (!dragStarted) {
1180 if (isFileList) {
1181 maybeStartTimer();
1182 return;
1183 }
1184
1185 Point p = e.getPoint();
1186
1187 if (pressedEvent != null &&
1188 table.rowAtPoint(p) == pressedRow &&
1189 table.columnAtPoint(p) == pressedCol &&
1190 table.editCellAt(pressedRow, pressedCol, pressedEvent)) {
1191
1192 setDispatchComponent(pressedEvent);
1193 repostEvent(pressedEvent);
1194
1195 // This may appear completely odd, but must be done for backward
1196 // compatibility reasons. Developers have been known to rely on
1197 // a call to shouldSelectCell after editing has begun.
1198 CellEditor ce = table.getCellEditor();
1199 if (ce != null) {
1200 ce.shouldSelectCell(pressedEvent);
1201 }
1202 }
1203 }
1204 }
1205
1206 public void mouseEntered(MouseEvent e) {}
1207
1208 public void mouseExited(MouseEvent e) {}
1209
1210 public void mouseMoved(MouseEvent e) {}
1211
1212 public void dragStarting(MouseEvent me) {
1213 dragStarted = true;
1214
1215 if (me.isControlDown() && isFileList) {
1216 table.getSelectionModel().addSelectionInterval(pressedRow,
1217 pressedRow);
1218 table.getColumnModel().getSelectionModel().
1219 addSelectionInterval(pressedCol, pressedCol);
1220 }
1221
1222 pressedEvent = null;
1223 }
1224
1225 public void mouseDragged(MouseEvent e) {
1226 if (SwingUtilities2.shouldIgnore(e, table)) {
1227 return;
1228 }
1229
1230 if (table.getDragEnabled() &&
1231 (DragRecognitionSupport.mouseDragged(e, this) || dragStarted)) {
1232
1233 return;
1234 }
1235
1236 repostEvent(e);
1237
1238 // Check isFileList:
1239 // Until we support drag-selection, dragging should not change
1240 // the selection (act like single-select).
1241 if (isFileList || table.isEditing()) {
1242 return;
1243 }
1244
1245 Point p = e.getPoint();
1246 int row = table.rowAtPoint(p);
1247 int column = table.columnAtPoint(p);
1248 // The autoscroller can generate drag events outside the
1249 // table's range.
1250 if ((column == -1) || (row == -1)) {
1251 return;
1252 }
1253
1254 table.changeSelection(row, column, e.isControlDown(), true);
1255 }
1256
1257
1258 // PropertyChangeListener
1259 public void propertyChange(PropertyChangeEvent event) {
1260 String changeName = event.getPropertyName();
1261
1262 if ("componentOrientation" == changeName) {
1263 InputMap inputMap = getInputMap(
1264 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
1265
1266 SwingUtilities.replaceUIInputMap(table,
1267 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
1268 inputMap);
1269
1270 JTableHeader header = table.getTableHeader();
1271 if (header != null) {
1272 header.setComponentOrientation(
1273 (ComponentOrientation)event.getNewValue());
1274 }
1275 } else if ("dropLocation" == changeName) {
1276 JTable.DropLocation oldValue = (JTable.DropLocation)event.getOldValue();
1277 repaintDropLocation(oldValue);
1278 repaintDropLocation(table.getDropLocation());
1279 } else if ("Table.isFileList" == changeName) {
1280 isFileList = Boolean.TRUE.equals(table.getClientProperty("Table.isFileList"));
1281 table.revalidate();
1282 table.repaint();
1283 if (isFileList) {
1284 table.getSelectionModel().addListSelectionListener(getHandler());
1285 } else {
1286 table.getSelectionModel().removeListSelectionListener(getHandler());
1287 timer = null;
1288 }
1289 } else if ("selectionModel" == changeName) {
1290 if (isFileList) {
1291 ListSelectionModel old = (ListSelectionModel)event.getOldValue();
1292 old.removeListSelectionListener(getHandler());
1293 table.getSelectionModel().addListSelectionListener(getHandler());
1294 }
1295 }
1296 }
1297
1298 private void repaintDropLocation(JTable.DropLocation loc) {
1299 if (loc == null) {
1300 return;
1301 }
1302
1303 if (!loc.isInsertRow() && !loc.isInsertColumn()) {
1304 Rectangle rect = table.getCellRect(loc.getRow(), loc.getColumn(), false);
1305 if (rect != null) {
1306 table.repaint(rect);
1307 }
1308 return;
1309 }
1310
1311 if (loc.isInsertRow()) {
1312 Rectangle rect = extendRect(getHDropLineRect(loc), true);
1313 if (rect != null) {
1314 table.repaint(rect);
1315 }
1316 }
1317
1318 if (loc.isInsertColumn()) {
1319 Rectangle rect = extendRect(getVDropLineRect(loc), false);
1320 if (rect != null) {
1321 table.repaint(rect);
1322 }
1323 }
1324 }
1325 }
1326
1327
1328 /*
1329 * Returns true if the given point is outside the preferredSize of the
1330 * item at the given row of the table. (Column must be 0).
1331 * Returns false if the "Table.isFileList" client property is not set.
1332 */
1333 private boolean pointOutsidePrefSize(int row, int column, Point p) {
1334 if (!isFileList) {
1335 return false;
1336 }
1337
1338 return SwingUtilities2.pointOutsidePrefSize(table, row, column, p);
1339 }
1340
1341//
1342// Factory methods for the Listeners
1343//
1344
1345 private Handler getHandler() {
1346 if (handler == null) {
1347 handler = new Handler();
1348 }
1349 return handler;
1350 }
1351
1352 /**
1353 * Creates the key listener for handling keyboard navigation in the JTable.
1354 */
1355 protected KeyListener createKeyListener() {
1356 return null;
1357 }
1358
1359 /**
1360 * Creates the focus listener for handling keyboard navigation in the JTable.
1361 */
1362 protected FocusListener createFocusListener() {
1363 return getHandler();
1364 }
1365
1366 /**
1367 * Creates the mouse listener for the JTable.
1368 */
1369 protected MouseInputListener createMouseInputListener() {
1370 return getHandler();
1371 }
1372
1373//
1374// The installation/uninstall procedures and support
1375//
1376
1377 public static ComponentUI createUI(JComponent c) {
1378 return new BasicTableUI();
1379 }
1380
1381// Installation
1382
1383 public void installUI(JComponent c) {
1384 table = (JTable)c;
1385
1386 rendererPane = new CellRendererPane();
1387 table.add(rendererPane);
1388 installDefaults();
1389 installDefaults2();
1390 installListeners();
1391 installKeyboardActions();
1392 }
1393
1394 /**
1395 * Initialize JTable properties, e.g. font, foreground, and background.
1396 * The font, foreground, and background properties are only set if their
1397 * current value is either null or a UIResource, other properties are set
1398 * if the current value is null.
1399 *
1400 * @see #installUI
1401 */
1402 protected void installDefaults() {
1403 LookAndFeel.installColorsAndFont(table, "Table.background",
1404 "Table.foreground", "Table.font");
1405 // JTable's original row height is 16. To correctly display the
1406 // contents on Linux we should have set it to 18, Windows 19 and
1407 // Solaris 20. As these values vary so much it's too hard to
1408 // be backward compatable and try to update the row height, we're
1409 // therefor NOT going to adjust the row height based on font. If the
1410 // developer changes the font, it's there responsability to update
1411 // the row height.
1412
1413 LookAndFeel.installProperty(table, "opaque", Boolean.TRUE);
1414
1415 Color sbg = table.getSelectionBackground();
1416 if (sbg == null || sbg instanceof UIResource) {
1417 table.setSelectionBackground(UIManager.getColor("Table.selectionBackground"));
1418 }
1419
1420 Color sfg = table.getSelectionForeground();
1421 if (sfg == null || sfg instanceof UIResource) {
1422 table.setSelectionForeground(UIManager.getColor("Table.selectionForeground"));
1423 }
1424
1425 Color gridColor = table.getGridColor();
1426 if (gridColor == null || gridColor instanceof UIResource) {
1427 table.setGridColor(UIManager.getColor("Table.gridColor"));
1428 }
1429
1430 // install the scrollpane border
1431 Container parent = table.getParent(); // should be viewport
1432 if (parent != null) {
1433 parent = parent.getParent(); // should be the scrollpane
1434 if (parent != null && parent instanceof JScrollPane) {
1435 LookAndFeel.installBorder((JScrollPane)parent, "Table.scrollPaneBorder");
1436 }
1437 }
1438
1439 isFileList = Boolean.TRUE.equals(table.getClientProperty("Table.isFileList"));
1440 }
1441
1442 private void installDefaults2() {
1443 TransferHandler th = table.getTransferHandler();
1444 if (th == null || th instanceof UIResource) {
1445 table.setTransferHandler(defaultTransferHandler);
1446 // default TransferHandler doesn't support drop
1447 // so we don't want drop handling
1448 if (table.getDropTarget() instanceof UIResource) {
1449 table.setDropTarget(null);
1450 }
1451 }
1452 }
1453
1454 /**
1455 * Attaches listeners to the JTable.
1456 */
1457 protected void installListeners() {
1458 focusListener = createFocusListener();
1459 keyListener = createKeyListener();
1460 mouseInputListener = createMouseInputListener();
1461
1462 table.addFocusListener(focusListener);
1463 table.addKeyListener(keyListener);
1464 table.addMouseListener(mouseInputListener);
1465 table.addMouseMotionListener(mouseInputListener);
1466 table.addPropertyChangeListener(getHandler());
1467 if (isFileList) {
1468 table.getSelectionModel().addListSelectionListener(getHandler());
1469 }
1470 }
1471
1472 /**
1473 * Register all keyboard actions on the JTable.
1474 */
1475 protected void installKeyboardActions() {
1476 LazyActionMap.installLazyActionMap(table, BasicTableUI.class,
1477 "Table.actionMap");
1478
1479 InputMap inputMap = getInputMap(JComponent.
1480 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
1481 SwingUtilities.replaceUIInputMap(table,
1482 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
1483 inputMap);
1484 }
1485
1486 InputMap getInputMap(int condition) {
1487 if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
1488 InputMap keyMap =
1489 (InputMap)DefaultLookup.get(table, this,
1490 "Table.ancestorInputMap");
1491 InputMap rtlKeyMap;
1492
1493 if (table.getComponentOrientation().isLeftToRight() ||
1494 ((rtlKeyMap = (InputMap)DefaultLookup.get(table, this,
1495 "Table.ancestorInputMap.RightToLeft")) == null)) {
1496 return keyMap;
1497 } else {
1498 rtlKeyMap.setParent(keyMap);
1499 return rtlKeyMap;
1500 }
1501 }
1502 return null;
1503 }
1504
1505 static void loadActionMap(LazyActionMap map) {
1506 // IMPORTANT: There is a very close coupling between the parameters
1507 // passed to the Actions constructor. Only certain parameter
1508 // combinations are supported. For example, the following Action would
1509 // not work as expected:
1510 // new Actions(Actions.NEXT_ROW_CELL, 1, 4, false, true)
1511 // Actions which move within the selection only (having a true
1512 // inSelection parameter) require that one of dx or dy be
1513 // zero and the other be -1 or 1. The point of this warning is
1514 // that you should be very careful about making sure a particular
1515 // combination of parameters is supported before changing or
1516 // adding anything here.
1517
1518 map.put(new Actions(Actions.NEXT_COLUMN, 1, 0,
1519 false, false));
1520 map.put(new Actions(Actions.NEXT_COLUMN_CHANGE_LEAD, 1, 0,
1521 false, false));
1522 map.put(new Actions(Actions.PREVIOUS_COLUMN, -1, 0,
1523 false, false));
1524 map.put(new Actions(Actions.PREVIOUS_COLUMN_CHANGE_LEAD, -1, 0,
1525 false, false));
1526 map.put(new Actions(Actions.NEXT_ROW, 0, 1,
1527 false, false));
1528 map.put(new Actions(Actions.NEXT_ROW_CHANGE_LEAD, 0, 1,
1529 false, false));
1530 map.put(new Actions(Actions.PREVIOUS_ROW, 0, -1,
1531 false, false));
1532 map.put(new Actions(Actions.PREVIOUS_ROW_CHANGE_LEAD, 0, -1,
1533 false, false));
1534 map.put(new Actions(Actions.NEXT_COLUMN_EXTEND_SELECTION,
1535 1, 0, true, false));
1536 map.put(new Actions(Actions.PREVIOUS_COLUMN_EXTEND_SELECTION,
1537 -1, 0, true, false));
1538 map.put(new Actions(Actions.NEXT_ROW_EXTEND_SELECTION,
1539 0, 1, true, false));
1540 map.put(new Actions(Actions.PREVIOUS_ROW_EXTEND_SELECTION,
1541 0, -1, true, false));
1542 map.put(new Actions(Actions.SCROLL_UP_CHANGE_SELECTION,
1543 false, false, true, false));
1544 map.put(new Actions(Actions.SCROLL_DOWN_CHANGE_SELECTION,
1545 false, true, true, false));
1546 map.put(new Actions(Actions.FIRST_COLUMN,
1547 false, false, false, true));
1548 map.put(new Actions(Actions.LAST_COLUMN,
1549 false, true, false, true));
1550
1551 map.put(new Actions(Actions.SCROLL_UP_EXTEND_SELECTION,
1552 true, false, true, false));
1553 map.put(new Actions(Actions.SCROLL_DOWN_EXTEND_SELECTION,
1554 true, true, true, false));
1555 map.put(new Actions(Actions.FIRST_COLUMN_EXTEND_SELECTION,
1556 true, false, false, true));
1557 map.put(new Actions(Actions.LAST_COLUMN_EXTEND_SELECTION,
1558 true, true, false, true));
1559
1560 map.put(new Actions(Actions.FIRST_ROW, false, false, true, true));
1561 map.put(new Actions(Actions.LAST_ROW, false, true, true, true));
1562
1563 map.put(new Actions(Actions.FIRST_ROW_EXTEND_SELECTION,
1564 true, false, true, true));
1565 map.put(new Actions(Actions.LAST_ROW_EXTEND_SELECTION,
1566 true, true, true, true));
1567
1568 map.put(new Actions(Actions.NEXT_COLUMN_CELL,
1569 1, 0, false, true));
1570 map.put(new Actions(Actions.PREVIOUS_COLUMN_CELL,
1571 -1, 0, false, true));
1572 map.put(new Actions(Actions.NEXT_ROW_CELL, 0, 1, false, true));
1573 map.put(new Actions(Actions.PREVIOUS_ROW_CELL,
1574 0, -1, false, true));
1575
1576 map.put(new Actions(Actions.SELECT_ALL));
1577 map.put(new Actions(Actions.CLEAR_SELECTION));
1578 map.put(new Actions(Actions.CANCEL_EDITING));
1579 map.put(new Actions(Actions.START_EDITING));
1580
1581 map.put(TransferHandler.getCutAction().getValue(Action.NAME),
1582 TransferHandler.getCutAction());
1583 map.put(TransferHandler.getCopyAction().getValue(Action.NAME),
1584 TransferHandler.getCopyAction());
1585 map.put(TransferHandler.getPasteAction().getValue(Action.NAME),
1586 TransferHandler.getPasteAction());
1587
1588 map.put(new Actions(Actions.SCROLL_LEFT_CHANGE_SELECTION,
1589 false, false, false, false));
1590 map.put(new Actions(Actions.SCROLL_RIGHT_CHANGE_SELECTION,
1591 false, true, false, false));
1592 map.put(new Actions(Actions.SCROLL_LEFT_EXTEND_SELECTION,
1593 true, false, false, false));
1594 map.put(new Actions(Actions.SCROLL_RIGHT_EXTEND_SELECTION,
1595 true, true, false, false));
1596
1597 map.put(new Actions(Actions.ADD_TO_SELECTION));
1598 map.put(new Actions(Actions.TOGGLE_AND_ANCHOR));
1599 map.put(new Actions(Actions.EXTEND_TO));
1600 map.put(new Actions(Actions.MOVE_SELECTION_TO));
1601 map.put(new Actions(Actions.FOCUS_HEADER));
1602 }
1603
1604// Uninstallation
1605
1606 public void uninstallUI(JComponent c) {
1607 uninstallDefaults();
1608 uninstallListeners();
1609 uninstallKeyboardActions();
1610
1611 table.remove(rendererPane);
1612 rendererPane = null;
1613 table = null;
1614 }
1615
1616 protected void uninstallDefaults() {
1617 if (table.getTransferHandler() instanceof UIResource) {
1618 table.setTransferHandler(null);
1619 }
1620 }
1621
1622 protected void uninstallListeners() {
1623 table.removeFocusListener(focusListener);
1624 table.removeKeyListener(keyListener);
1625 table.removeMouseListener(mouseInputListener);
1626 table.removeMouseMotionListener(mouseInputListener);
1627 table.removePropertyChangeListener(getHandler());
1628 if (isFileList) {
1629 table.getSelectionModel().removeListSelectionListener(getHandler());
1630 }
1631
1632 focusListener = null;
1633 keyListener = null;
1634 mouseInputListener = null;
1635 handler = null;
1636 }
1637
1638 protected void uninstallKeyboardActions() {
1639 SwingUtilities.replaceUIInputMap(table, JComponent.
1640 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
1641 SwingUtilities.replaceUIActionMap(table, null);
1642 }
1643
1644 /**
1645 * Returns the baseline.
1646 *
1647 * @throws NullPointerException {@inheritDoc}
1648 * @throws IllegalArgumentException {@inheritDoc}
1649 * @see javax.swing.JComponent#getBaseline(int, int)
1650 * @since 1.6
1651 */
1652 public int getBaseline(JComponent c, int width, int height) {
1653 super.getBaseline(c, width, height);
1654 UIDefaults lafDefaults = UIManager.getLookAndFeelDefaults();
1655 Component renderer = (Component)lafDefaults.get(
1656 BASELINE_COMPONENT_KEY);
1657 if (renderer == null) {
1658 DefaultTableCellRenderer tcr = new DefaultTableCellRenderer();
1659 renderer = tcr.getTableCellRendererComponent(
1660 table, "a", false, false, -1, -1);
1661 lafDefaults.put(BASELINE_COMPONENT_KEY, renderer);
1662 }
1663 renderer.setFont(table.getFont());
1664 int rowMargin = table.getRowMargin();
1665 return renderer.getBaseline(Integer.MAX_VALUE, table.getRowHeight() -
1666 rowMargin) + rowMargin / 2;
1667 }
1668
1669 /**
1670 * Returns an enum indicating how the baseline of the component
1671 * changes as the size changes.
1672 *
1673 * @throws NullPointerException {@inheritDoc}
1674 * @see javax.swing.JComponent#getBaseline(int, int)
1675 * @since 1.6
1676 */
1677 public Component.BaselineResizeBehavior getBaselineResizeBehavior(
1678 JComponent c) {
1679 super.getBaselineResizeBehavior(c);
1680 return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
1681 }
1682
1683//
1684// Size Methods
1685//
1686
1687 private Dimension createTableSize(long width) {
1688 int height = 0;
1689 int rowCount = table.getRowCount();
1690 if (rowCount > 0 && table.getColumnCount() > 0) {
1691 Rectangle r = table.getCellRect(rowCount-1, 0, true);
1692 height = r.y + r.height;
1693 }
1694 // Width is always positive. The call to abs() is a workaround for
1695 // a bug in the 1.1.6 JIT on Windows.
1696 long tmp = Math.abs(width);
1697 if (tmp > Integer.MAX_VALUE) {
1698 tmp = Integer.MAX_VALUE;
1699 }
1700 return new Dimension((int)tmp, height);
1701 }
1702
1703 /**
1704 * Return the minimum size of the table. The minimum height is the
1705 * row height times the number of rows.
1706 * The minimum width is the sum of the minimum widths of each column.
1707 */
1708 public Dimension getMinimumSize(JComponent c) {
1709 long width = 0;
1710 Enumeration enumeration = table.getColumnModel().getColumns();
1711 while (enumeration.hasMoreElements()) {
1712 TableColumn aColumn = (TableColumn)enumeration.nextElement();
1713 width = width + aColumn.getMinWidth();
1714 }
1715 return createTableSize(width);
1716 }
1717
1718 /**
1719 * Return the preferred size of the table. The preferred height is the
1720 * row height times the number of rows.
1721 * The preferred width is the sum of the preferred widths of each column.
1722 */
1723 public Dimension getPreferredSize(JComponent c) {
1724 long width = 0;
1725 Enumeration enumeration = table.getColumnModel().getColumns();
1726 while (enumeration.hasMoreElements()) {
1727 TableColumn aColumn = (TableColumn)enumeration.nextElement();
1728 width = width + aColumn.getPreferredWidth();
1729 }
1730 return createTableSize(width);
1731 }
1732
1733 /**
1734 * Return the maximum size of the table. The maximum height is the
1735 * row heighttimes the number of rows.
1736 * The maximum width is the sum of the maximum widths of each column.
1737 */
1738 public Dimension getMaximumSize(JComponent c) {
1739 long width = 0;
1740 Enumeration enumeration = table.getColumnModel().getColumns();
1741 while (enumeration.hasMoreElements()) {
1742 TableColumn aColumn = (TableColumn)enumeration.nextElement();
1743 width = width + aColumn.getMaxWidth();
1744 }
1745 return createTableSize(width);
1746 }
1747
1748//
1749// Paint methods and support
1750//
1751
1752 /** Paint a representation of the <code>table</code> instance
1753 * that was set in installUI().
1754 */
1755 public void paint(Graphics g, JComponent c) {
1756 Rectangle clip = g.getClipBounds();
1757
1758 Rectangle bounds = table.getBounds();
1759 // account for the fact that the graphics has already been translated
1760 // into the table's bounds
1761 bounds.x = bounds.y = 0;
1762
1763 if (table.getRowCount() <= 0 || table.getColumnCount() <= 0 ||
1764 // this check prevents us from painting the entire table
1765 // when the clip doesn't intersect our bounds at all
1766 !bounds.intersects(clip)) {
1767
1768 paintDropLines(g);
1769 return;
1770 }
1771
1772 boolean ltr = table.getComponentOrientation().isLeftToRight();
1773
1774 Point upperLeft = clip.getLocation();
1775 Point lowerRight = new Point(clip.x + clip.width - 1,
1776 clip.y + clip.height - 1);
1777
1778 int rMin = table.rowAtPoint(upperLeft);
1779 int rMax = table.rowAtPoint(lowerRight);
1780 // This should never happen (as long as our bounds intersect the clip,
1781 // which is why we bail above if that is the case).
1782 if (rMin == -1) {
1783 rMin = 0;
1784 }
1785 // If the table does not have enough rows to fill the view we'll get -1.
1786 // (We could also get -1 if our bounds don't intersect the clip,
1787 // which is why we bail above if that is the case).
1788 // Replace this with the index of the last row.
1789 if (rMax == -1) {
1790 rMax = table.getRowCount()-1;
1791 }
1792
1793 int cMin = table.columnAtPoint(ltr ? upperLeft : lowerRight);
1794 int cMax = table.columnAtPoint(ltr ? lowerRight : upperLeft);
1795 // This should never happen.
1796 if (cMin == -1) {
1797 cMin = 0;
1798 }
1799 // If the table does not have enough columns to fill the view we'll get -1.
1800 // Replace this with the index of the last column.
1801 if (cMax == -1) {
1802 cMax = table.getColumnCount()-1;
1803 }
1804
1805 // Paint the grid.
1806 paintGrid(g, rMin, rMax, cMin, cMax);
1807
1808 // Paint the cells.
1809 paintCells(g, rMin, rMax, cMin, cMax);
1810
1811 paintDropLines(g);
1812 }
1813
1814 private void paintDropLines(Graphics g) {
1815 JTable.DropLocation loc = table.getDropLocation();
1816 if (loc == null) {
1817 return;
1818 }
1819
1820 Color color = UIManager.getColor("Table.dropLineColor");
1821 Color shortColor = UIManager.getColor("Table.dropLineShortColor");
1822 if (color == null && shortColor == null) {
1823 return;
1824 }
1825
1826 Rectangle rect;
1827
1828 rect = getHDropLineRect(loc);
1829 if (rect != null) {
1830 int x = rect.x;
1831 int w = rect.width;
1832 if (color != null) {
1833 extendRect(rect, true);
1834 g.setColor(color);
1835 g.fillRect(rect.x, rect.y, rect.width, rect.height);
1836 }
1837 if (!loc.isInsertColumn() && shortColor != null) {
1838 g.setColor(shortColor);
1839 g.fillRect(x, rect.y, w, rect.height);
1840 }
1841 }
1842
1843 rect = getVDropLineRect(loc);
1844 if (rect != null) {
1845 int y = rect.y;
1846 int h = rect.height;
1847 if (color != null) {
1848 extendRect(rect, false);
1849 g.setColor(color);
1850 g.fillRect(rect.x, rect.y, rect.width, rect.height);
1851 }
1852 if (!loc.isInsertRow() && shortColor != null) {
1853 g.setColor(shortColor);
1854 g.fillRect(rect.x, y, rect.width, h);
1855 }
1856 }
1857 }
1858
1859 private Rectangle getHDropLineRect(JTable.DropLocation loc) {
1860 if (!loc.isInsertRow()) {
1861 return null;
1862 }
1863
1864 int row = loc.getRow();
1865 int col = loc.getColumn();
1866 if (col >= table.getColumnCount()) {
1867 col--;
1868 }
1869
1870 Rectangle rect = table.getCellRect(row, col, true);
1871
1872 if (row >= table.getRowCount()) {
1873 row--;
1874 Rectangle prevRect = table.getCellRect(row, col, true);
1875 rect.y = prevRect.y + prevRect.height;
1876 }
1877
1878 if (rect.y == 0) {
1879 rect.y = -1;
1880 } else {
1881 rect.y -= 2;
1882 }
1883
1884 rect.height = 3;
1885
1886 return rect;
1887 }
1888
1889 private Rectangle getVDropLineRect(JTable.DropLocation loc) {
1890 if (!loc.isInsertColumn()) {
1891 return null;
1892 }
1893
1894 boolean ltr = table.getComponentOrientation().isLeftToRight();
1895 int col = loc.getColumn();
1896 Rectangle rect = table.getCellRect(loc.getRow(), col, true);
1897
1898 if (col >= table.getColumnCount()) {
1899 col--;
1900 rect = table.getCellRect(loc.getRow(), col, true);
1901 if (ltr) {
1902 rect.x = rect.x + rect.width;
1903 }
1904 } else if (!ltr) {
1905 rect.x = rect.x + rect.width;
1906 }
1907
1908 if (rect.x == 0) {
1909 rect.x = -1;
1910 } else {
1911 rect.x -= 2;
1912 }
1913
1914 rect.width = 3;
1915
1916 return rect;
1917 }
1918
1919 private Rectangle extendRect(Rectangle rect, boolean horizontal) {
1920 if (rect == null) {
1921 return rect;
1922 }
1923
1924 if (horizontal) {
1925 rect.x = 0;
1926 rect.width = table.getWidth();
1927 } else {
1928 rect.y = 0;
1929
1930 if (table.getRowCount() != 0) {
1931 Rectangle lastRect = table.getCellRect(table.getRowCount() - 1, 0, true);
1932 rect.height = lastRect.y + lastRect.height;
1933 } else {
1934 rect.height = table.getHeight();
1935 }
1936 }
1937
1938 return rect;
1939 }
1940
1941 /*
1942 * Paints the grid lines within <I>aRect</I>, using the grid
1943 * color set with <I>setGridColor</I>. Paints vertical lines
1944 * if <code>getShowVerticalLines()</code> returns true and paints
1945 * horizontal lines if <code>getShowHorizontalLines()</code>
1946 * returns true.
1947 */
1948 private void paintGrid(Graphics g, int rMin, int rMax, int cMin, int cMax) {
1949 g.setColor(table.getGridColor());
1950
1951 Rectangle minCell = table.getCellRect(rMin, cMin, true);
1952 Rectangle maxCell = table.getCellRect(rMax, cMax, true);
1953 Rectangle damagedArea = minCell.union( maxCell );
1954
1955 if (table.getShowHorizontalLines()) {
1956 int tableWidth = damagedArea.x + damagedArea.width;
1957 int y = damagedArea.y;
1958 for (int row = rMin; row <= rMax; row++) {
1959 y += table.getRowHeight(row);
1960 g.drawLine(damagedArea.x, y - 1, tableWidth - 1, y - 1);
1961 }
1962 }
1963 if (table.getShowVerticalLines()) {
1964 TableColumnModel cm = table.getColumnModel();
1965 int tableHeight = damagedArea.y + damagedArea.height;
1966 int x;
1967 if (table.getComponentOrientation().isLeftToRight()) {
1968 x = damagedArea.x;
1969 for (int column = cMin; column <= cMax; column++) {
1970 int w = cm.getColumn(column).getWidth();
1971 x += w;
1972 g.drawLine(x - 1, 0, x - 1, tableHeight - 1);
1973 }
1974 } else {
1975 x = damagedArea.x;
1976 for (int column = cMax; column >= cMin; column--) {
1977 int w = cm.getColumn(column).getWidth();
1978 x += w;
1979 g.drawLine(x - 1, 0, x - 1, tableHeight - 1);
1980 }
1981 }
1982 }
1983 }
1984
1985 private int viewIndexForColumn(TableColumn aColumn) {
1986 TableColumnModel cm = table.getColumnModel();
1987 for (int column = 0; column < cm.getColumnCount(); column++) {
1988 if (cm.getColumn(column) == aColumn) {
1989 return column;
1990 }
1991 }
1992 return -1;
1993 }
1994
1995 private void paintCells(Graphics g, int rMin, int rMax, int cMin, int cMax) {
1996 JTableHeader header = table.getTableHeader();
1997 TableColumn draggedColumn = (header == null) ? null : header.getDraggedColumn();
1998
1999 TableColumnModel cm = table.getColumnModel();
2000 int columnMargin = cm.getColumnMargin();
2001
2002 Rectangle cellRect;
2003 TableColumn aColumn;
2004 int columnWidth;
2005 if (table.getComponentOrientation().isLeftToRight()) {
2006 for(int row = rMin; row <= rMax; row++) {
2007 cellRect = table.getCellRect(row, cMin, false);
2008 for(int column = cMin; column <= cMax; column++) {
2009 aColumn = cm.getColumn(column);
2010 columnWidth = aColumn.getWidth();
2011 cellRect.width = columnWidth - columnMargin;
2012 if (aColumn != draggedColumn) {
2013 paintCell(g, cellRect, row, column);
2014 }
2015 cellRect.x += columnWidth;
2016 }
2017 }
2018 } else {
2019 for(int row = rMin; row <= rMax; row++) {
2020 cellRect = table.getCellRect(row, cMin, false);
2021 aColumn = cm.getColumn(cMin);
2022 if (aColumn != draggedColumn) {
2023 columnWidth = aColumn.getWidth();
2024 cellRect.width = columnWidth - columnMargin;
2025 paintCell(g, cellRect, row, cMin);
2026 }
2027 for(int column = cMin+1; column <= cMax; column++) {
2028 aColumn = cm.getColumn(column);
2029 columnWidth = aColumn.getWidth();
2030 cellRect.width = columnWidth - columnMargin;
2031 cellRect.x -= columnWidth;
2032 if (aColumn != draggedColumn) {
2033 paintCell(g, cellRect, row, column);
2034 }
2035 }
2036 }
2037 }
2038
2039 // Paint the dragged column if we are dragging.
2040 if (draggedColumn != null) {
2041 paintDraggedArea(g, rMin, rMax, draggedColumn, header.getDraggedDistance());
2042 }
2043
2044 // Remove any renderers that may be left in the rendererPane.
2045 rendererPane.removeAll();
2046 }
2047
2048 private void paintDraggedArea(Graphics g, int rMin, int rMax, TableColumn draggedColumn, int distance) {
2049 int draggedColumnIndex = viewIndexForColumn(draggedColumn);
2050
2051 Rectangle minCell = table.getCellRect(rMin, draggedColumnIndex, true);
2052 Rectangle maxCell = table.getCellRect(rMax, draggedColumnIndex, true);
2053
2054 Rectangle vacatedColumnRect = minCell.union(maxCell);
2055
2056 // Paint a gray well in place of the moving column.
2057 g.setColor(table.getParent().getBackground());
2058 g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
2059 vacatedColumnRect.width, vacatedColumnRect.height);
2060
2061 // Move to the where the cell has been dragged.
2062 vacatedColumnRect.x += distance;
2063
2064 // Fill the background.
2065 g.setColor(table.getBackground());
2066 g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
2067 vacatedColumnRect.width, vacatedColumnRect.height);
2068
2069 // Paint the vertical grid lines if necessary.
2070 if (table.getShowVerticalLines()) {
2071 g.setColor(table.getGridColor());
2072 int x1 = vacatedColumnRect.x;
2073 int y1 = vacatedColumnRect.y;
2074 int x2 = x1 + vacatedColumnRect.width - 1;
2075 int y2 = y1 + vacatedColumnRect.height - 1;
2076 // Left
2077 g.drawLine(x1-1, y1, x1-1, y2);
2078 // Right
2079 g.drawLine(x2, y1, x2, y2);
2080 }
2081
2082 for(int row = rMin; row <= rMax; row++) {
2083 // Render the cell value
2084 Rectangle r = table.getCellRect(row, draggedColumnIndex, false);
2085 r.x += distance;
2086 paintCell(g, r, row, draggedColumnIndex);
2087
2088 // Paint the (lower) horizontal grid line if necessary.
2089 if (table.getShowHorizontalLines()) {
2090 g.setColor(table.getGridColor());
2091 Rectangle rcr = table.getCellRect(row, draggedColumnIndex, true);
2092 rcr.x += distance;
2093 int x1 = rcr.x;
2094 int y1 = rcr.y;
2095 int x2 = x1 + rcr.width - 1;
2096 int y2 = y1 + rcr.height - 1;
2097 g.drawLine(x1, y2, x2, y2);
2098 }
2099 }
2100 }
2101
2102 private void paintCell(Graphics g, Rectangle cellRect, int row, int column) {
2103 if (table.isEditing() && table.getEditingRow()==row &&
2104 table.getEditingColumn()==column) {
2105 Component component = table.getEditorComponent();
2106 component.setBounds(cellRect);
2107 component.validate();
2108 }
2109 else {
2110 TableCellRenderer renderer = table.getCellRenderer(row, column);
2111 Component component = table.prepareRenderer(renderer, row, column);
2112 rendererPane.paintComponent(g, component, table, cellRect.x, cellRect.y,
2113 cellRect.width, cellRect.height, true);
2114 }
2115 }
2116
2117 private static int getAdjustedLead(JTable table,
2118 boolean row,
2119 ListSelectionModel model) {
2120
2121 int index = model.getLeadSelectionIndex();
2122 int compare = row ? table.getRowCount() : table.getColumnCount();
2123 return index < compare ? index : -1;
2124 }
2125
2126 private static int getAdjustedLead(JTable table, boolean row) {
2127 return row ? getAdjustedLead(table, row, table.getSelectionModel())
2128 : getAdjustedLead(table, row, table.getColumnModel().getSelectionModel());
2129 }
2130
2131
2132 private static final TransferHandler defaultTransferHandler = new TableTransferHandler();
2133
2134 static class TableTransferHandler extends TransferHandler implements UIResource {
2135
2136 /**
2137 * Create a Transferable to use as the source for a data transfer.
2138 *
2139 * @param c The component holding the data to be transfered. This
2140 * argument is provided to enable sharing of TransferHandlers by
2141 * multiple components.
2142 * @return The representation of the data to be transfered.
2143 *
2144 */
2145 protected Transferable createTransferable(JComponent c) {
2146 if (c instanceof JTable) {
2147 JTable table = (JTable) c;
2148 int[] rows;
2149 int[] cols;
2150
2151 if (!table.getRowSelectionAllowed() && !table.getColumnSelectionAllowed()) {
2152 return null;
2153 }
2154
2155 if (!table.getRowSelectionAllowed()) {
2156 int rowCount = table.getRowCount();
2157
2158 rows = new int[rowCount];
2159 for (int counter = 0; counter < rowCount; counter++) {
2160 rows[counter] = counter;
2161 }
2162 } else {
2163 rows = table.getSelectedRows();
2164 }
2165
2166 if (!table.getColumnSelectionAllowed()) {
2167 int colCount = table.getColumnCount();
2168
2169 cols = new int[colCount];
2170 for (int counter = 0; counter < colCount; counter++) {
2171 cols[counter] = counter;
2172 }
2173 } else {
2174 cols = table.getSelectedColumns();
2175 }
2176
2177 if (rows == null || cols == null || rows.length == 0 || cols.length == 0) {
2178 return null;
2179 }
2180
2181 StringBuffer plainBuf = new StringBuffer();
2182 StringBuffer htmlBuf = new StringBuffer();
2183
2184 htmlBuf.append("<html>\n<body>\n<table>\n");
2185
2186 for (int row = 0; row < rows.length; row++) {
2187 htmlBuf.append("<tr>\n");
2188 for (int col = 0; col < cols.length; col++) {
2189 Object obj = table.getValueAt(rows[row], cols[col]);
2190 String val = ((obj == null) ? "" : obj.toString());
2191 plainBuf.append(val + "\t");
2192 htmlBuf.append(" <td>" + val + "</td>\n");
2193 }
2194 // we want a newline at the end of each line and not a tab
2195 plainBuf.deleteCharAt(plainBuf.length() - 1).append("\n");
2196 htmlBuf.append("</tr>\n");
2197 }
2198
2199 // remove the last newline
2200 plainBuf.deleteCharAt(plainBuf.length() - 1);
2201 htmlBuf.append("</table>\n</body>\n</html>");
2202
2203 return new BasicTransferable(plainBuf.toString(), htmlBuf.toString());
2204 }
2205
2206 return null;
2207 }
2208
2209 public int getSourceActions(JComponent c) {
2210 return COPY;
2211 }
2212
2213 }
2214} // End of Class BasicTableUI