History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

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

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

Unified · Split

java/pkg/javax/swing/plaf/basic/BasicTableUI.java+2217−0
@@ -0,0 +1,2217 @@
1/*
2 * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
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 class should be treated as a &quot;protected&quot; inner class.
734 * Instantiate it only within subclasses of {@code BasicTableUI}.
735 * <p>As of Java 2 platform v1.3 this class is no longer used.
736 * Instead <code>JTable</code>
737 * overrides <code>processKeyBinding</code> to dispatch the event to
738 * the current <code>TableCellEditor</code>.
739 */
740 public class KeyHandler implements KeyListener {
741 // NOTE: This class exists only for backward compatability. All
742 // its functionality has been moved into Handler. If you need to add
743 // new functionality add it to the Handler, but make sure this
744 // class calls into the Handler.
745 public void keyPressed(KeyEvent e) {
746 getHandler().keyPressed(e);
747 }
748
749 public void keyReleased(KeyEvent e) {
750 getHandler().keyReleased(e);
751 }
752
753 public void keyTyped(KeyEvent e) {
754 getHandler().keyTyped(e);
755 }
756 }
757
758//
759// The Table's focus listener
760//
761
762 /**
763 * This class should be treated as a &quot;protected&quot; inner class.
764 * Instantiate it only within subclasses of {@code BasicTableUI}.
765 */
766 public class FocusHandler implements FocusListener {
767 // NOTE: This class exists only for backward compatability. All
768 // its functionality has been moved into Handler. If you need to add
769 // new functionality add it to the Handler, but make sure this
770 // class calls into the Handler.
771 public void focusGained(FocusEvent e) {
772 getHandler().focusGained(e);
773 }
774
775 public void focusLost(FocusEvent e) {
776 getHandler().focusLost(e);
777 }
778 }
779
780//
781// The Table's mouse and mouse motion listeners
782//
783
784 /**
785 * This class should be treated as a &quot;protected&quot; inner class.
786 * Instantiate it only within subclasses of BasicTableUI.
787 */
788 public class MouseInputHandler implements MouseInputListener {
789 // NOTE: This class exists only for backward compatability. All
790 // its functionality has been moved into Handler. If you need to add
791 // new functionality add it to the Handler, but make sure this
792 // class calls into the Handler.
793 public void mouseClicked(MouseEvent e) {
794 getHandler().mouseClicked(e);
795 }
796
797 public void mousePressed(MouseEvent e) {
798 getHandler().mousePressed(e);
799 }
800
801 public void mouseReleased(MouseEvent e) {
802 getHandler().mouseReleased(e);
803 }
804
805 public void mouseEntered(MouseEvent e) {
806 getHandler().mouseEntered(e);
807 }
808
809 public void mouseExited(MouseEvent e) {
810 getHandler().mouseExited(e);
811 }
812
813 public void mouseMoved(MouseEvent e) {
814 getHandler().mouseMoved(e);
815 }
816
817 public void mouseDragged(MouseEvent e) {
818 getHandler().mouseDragged(e);
819 }
820 }
821
822 private class Handler implements FocusListener, MouseInputListener,
823 PropertyChangeListener, ListSelectionListener, ActionListener,
824 BeforeDrag {
825
826 // FocusListener
827 private void repaintLeadCell( ) {
828 int lr = getAdjustedLead(table, true);
829 int lc = getAdjustedLead(table, false);
830
831 if (lr < 0 || lc < 0) {
832 return;
833 }
834
835 Rectangle dirtyRect = table.getCellRect(lr, lc, false);
836 table.repaint(dirtyRect);
837 }
838
839 public void focusGained(FocusEvent e) {
840 repaintLeadCell();
841 }
842
843 public void focusLost(FocusEvent e) {
844 repaintLeadCell();
845 }
846
847
848 // KeyListener
849 public void keyPressed(KeyEvent e) { }
850
851 public void keyReleased(KeyEvent e) { }
852
853 public void keyTyped(KeyEvent e) {
854 KeyStroke keyStroke = KeyStroke.getKeyStroke(e.getKeyChar(),
855 e.getModifiers());
856
857 // We register all actions using ANCESTOR_OF_FOCUSED_COMPONENT
858 // which means that we might perform the appropriate action
859 // in the table and then forward it to the editor if the editor
860 // had focus. Make sure this doesn't happen by checking our
861 // InputMaps.
862 InputMap map = table.getInputMap(JComponent.WHEN_FOCUSED);
863 if (map != null && map.get(keyStroke) != null) {
864 return;
865 }
866 map = table.getInputMap(JComponent.
867 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
868 if (map != null && map.get(keyStroke) != null) {
869 return;
870 }
871
872 keyStroke = KeyStroke.getKeyStrokeForEvent(e);
873
874 // The AWT seems to generate an unconsumed \r event when
875 // ENTER (\n) is pressed.
876 if (e.getKeyChar() == '\r') {
877 return;
878 }
879
880 int leadRow = getAdjustedLead(table, true);
881 int leadColumn = getAdjustedLead(table, false);
882 if (leadRow != -1 && leadColumn != -1 && !table.isEditing()) {
883 if (!table.editCellAt(leadRow, leadColumn)) {
884 return;
885 }
886 }
887
888 // Forwarding events this way seems to put the component
889 // in a state where it believes it has focus. In reality
890 // the table retains focus - though it is difficult for
891 // a user to tell, since the caret is visible and flashing.
892
893 // Calling table.requestFocus() here, to get the focus back to
894 // the table, seems to have no effect.
895
896 Component editorComp = table.getEditorComponent();
897 if (table.isEditing() && editorComp != null) {
898 if (editorComp instanceof JComponent) {
899 JComponent component = (JComponent)editorComp;
900 map = component.getInputMap(JComponent.WHEN_FOCUSED);
901 Object binding = (map != null) ? map.get(keyStroke) : null;
902 if (binding == null) {
903 map = component.getInputMap(JComponent.
904 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
905 binding = (map != null) ? map.get(keyStroke) : null;
906 }
907 if (binding != null) {
908 ActionMap am = component.getActionMap();
909 Action action = (am != null) ? am.get(binding) : null;
910 if (action != null && SwingUtilities.
911 notifyAction(action, keyStroke, e, component,
912 e.getModifiers())) {
913 e.consume();
914 }
915 }
916 }
917 }
918 }
919
920
921 // MouseInputListener
922
923 // Component receiving mouse events during editing.
924 // May not be editorComponent.
925 private Component dispatchComponent;
926
927 public void mouseClicked(MouseEvent e) {}
928
929 private void setDispatchComponent(MouseEvent e) {
930 Component editorComponent = table.getEditorComponent();
931 Point p = e.getPoint();
932 Point p2 = SwingUtilities.convertPoint(table, p, editorComponent);
933 dispatchComponent =
934 SwingUtilities.getDeepestComponentAt(editorComponent,
935 p2.x, p2.y);
936 SwingUtilities2.setSkipClickCount(dispatchComponent,
937 e.getClickCount() - 1);
938 }
939
940 private boolean repostEvent(MouseEvent e) {
941 // Check for isEditing() in case another event has
942 // caused the editor to be removed. See bug #4306499.
943 if (dispatchComponent == null || !table.isEditing()) {
944 return false;
945 }
946 MouseEvent e2 = SwingUtilities.convertMouseEvent(table, e,
947 dispatchComponent);
948 dispatchComponent.dispatchEvent(e2);
949 return true;
950 }
951
952 private void setValueIsAdjusting(boolean flag) {
953 table.getSelectionModel().setValueIsAdjusting(flag);
954 table.getColumnModel().getSelectionModel().
955 setValueIsAdjusting(flag);
956 }
957
958 // The row and column where the press occurred and the
959 // press event itself
960 private int pressedRow;
961 private int pressedCol;
962 private MouseEvent pressedEvent;
963
964 // Whether or not the mouse press (which is being considered as part
965 // of a drag sequence) also caused the selection change to be fully
966 // processed.
967 private boolean dragPressDidSelection;
968
969 // Set to true when a drag gesture has been fully recognized and DnD
970 // begins. Use this to ignore further mouse events which could be
971 // delivered if DnD is cancelled (via ESCAPE for example)
972 private boolean dragStarted;
973
974 // Whether or not we should start the editing timer on release
975 private boolean shouldStartTimer;
976
977 // To cache the return value of pointOutsidePrefSize since we use
978 // it multiple times.
979 private boolean outsidePrefSize;
980
981 // Used to delay the start of editing.
982 private Timer timer = null;
983
984 private boolean canStartDrag() {
985 if (pressedRow == -1 || pressedCol == -1) {
986 return false;
987 }
988
989 if (isFileList) {
990 return !outsidePrefSize;
991 }
992
993 // if this is a single selection table
994 if ((table.getSelectionModel().getSelectionMode() ==
995 ListSelectionModel.SINGLE_SELECTION) &&
996 (table.getColumnModel().getSelectionModel().getSelectionMode() ==
997 ListSelectionModel.SINGLE_SELECTION)) {
998
999 return true;
1000 }
1001
1002 return table.isCellSelected(pressedRow, pressedCol);
1003 }
1004
1005 public void mousePressed(MouseEvent e) {
1006 if (SwingUtilities2.shouldIgnore(e, table)) {
1007 return;
1008 }
1009
1010 if (table.isEditing() && !table.getCellEditor().stopCellEditing()) {
1011 Component editorComponent = table.getEditorComponent();
1012 if (editorComponent != null && !editorComponent.hasFocus()) {
1013 SwingUtilities2.compositeRequestFocus(editorComponent);
1014 }
1015 return;
1016 }
1017
1018 Point p = e.getPoint();
1019 pressedRow = table.rowAtPoint(p);
1020 pressedCol = table.columnAtPoint(p);
1021 outsidePrefSize = pointOutsidePrefSize(pressedRow, pressedCol, p);
1022
1023 if (isFileList) {
1024 shouldStartTimer =
1025 table.isCellSelected(pressedRow, pressedCol) &&
1026 !e.isShiftDown() &&
1027 !BasicGraphicsUtils.isMenuShortcutKeyDown(e) &&
1028 !outsidePrefSize;
1029 }
1030
1031 if (table.getDragEnabled()) {
1032 mousePressedDND(e);
1033 } else {
1034 SwingUtilities2.adjustFocus(table);
1035 if (!isFileList) {
1036 setValueIsAdjusting(true);
1037 }
1038 adjustSelection(e);
1039 }
1040 }
1041
1042 private void mousePressedDND(MouseEvent e) {
1043 pressedEvent = e;
1044 boolean grabFocus = true;
1045 dragStarted = false;
1046
1047 if (canStartDrag() && DragRecognitionSupport.mousePressed(e)) {
1048
1049 dragPressDidSelection = false;
1050
1051 if (BasicGraphicsUtils.isMenuShortcutKeyDown(e) && isFileList) {
1052 // do nothing for control - will be handled on release
1053 // or when drag starts
1054 return;
1055 } else if (!e.isShiftDown() && table.isCellSelected(pressedRow, pressedCol)) {
1056 // clicking on something that's already selected
1057 // and need to make it the lead now
1058 table.getSelectionModel().addSelectionInterval(pressedRow,
1059 pressedRow);
1060 table.getColumnModel().getSelectionModel().
1061 addSelectionInterval(pressedCol, pressedCol);
1062
1063 return;
1064 }
1065
1066 dragPressDidSelection = true;
1067
1068 // could be a drag initiating event - don't grab focus
1069 grabFocus = false;
1070 } else if (!isFileList) {
1071 // When drag can't happen, mouse drags might change the selection in the table
1072 // so we want the isAdjusting flag to be set
1073 setValueIsAdjusting(true);
1074 }
1075
1076 if (grabFocus) {
1077 SwingUtilities2.adjustFocus(table);
1078 }
1079
1080 adjustSelection(e);
1081 }
1082
1083 private void adjustSelection(MouseEvent e) {
1084 // Fix for 4835633
1085 if (outsidePrefSize) {
1086 // If shift is down in multi-select, we should just return.
1087 // For single select or non-shift-click, clear the selection
1088 if (e.getID() == MouseEvent.MOUSE_PRESSED &&
1089 (!e.isShiftDown() ||
1090 table.getSelectionModel().getSelectionMode() ==
1091 ListSelectionModel.SINGLE_SELECTION)) {
1092 table.clearSelection();
1093 TableCellEditor tce = table.getCellEditor();
1094 if (tce != null) {
1095 tce.stopCellEditing();
1096 }
1097 }
1098 return;
1099 }
1100 // The autoscroller can generate drag events outside the
1101 // table's range.
1102 if ((pressedCol == -1) || (pressedRow == -1)) {
1103 return;
1104 }
1105
1106 boolean dragEnabled = table.getDragEnabled();
1107
1108 if (!dragEnabled && !isFileList && table.editCellAt(pressedRow, pressedCol, e)) {
1109 setDispatchComponent(e);
1110 repostEvent(e);
1111 }
1112
1113 CellEditor editor = table.getCellEditor();
1114 if (dragEnabled || editor == null || editor.shouldSelectCell(e)) {
1115 table.changeSelection(pressedRow, pressedCol,
1116 BasicGraphicsUtils.isMenuShortcutKeyDown(e),
1117 e.isShiftDown());
1118 }
1119 }
1120
1121 public void valueChanged(ListSelectionEvent e) {
1122 if (timer != null) {
1123 timer.stop();
1124 timer = null;
1125 }
1126 }
1127
1128 public void actionPerformed(ActionEvent ae) {
1129 table.editCellAt(pressedRow, pressedCol, null);
1130 Component editorComponent = table.getEditorComponent();
1131 if (editorComponent != null && !editorComponent.hasFocus()) {
1132 SwingUtilities2.compositeRequestFocus(editorComponent);
1133 }
1134 return;
1135 }
1136
1137 private void maybeStartTimer() {
1138 if (!shouldStartTimer) {
1139 return;
1140 }
1141
1142 if (timer == null) {
1143 timer = new Timer(1200, this);
1144 timer.setRepeats(false);
1145 }
1146
1147 timer.start();
1148 }
1149
1150 public void mouseReleased(MouseEvent e) {
1151 if (SwingUtilities2.shouldIgnore(e, table)) {
1152 return;
1153 }
1154
1155 if (table.getDragEnabled()) {
1156 mouseReleasedDND(e);
1157 } else {
1158 if (isFileList) {
1159 maybeStartTimer();
1160 }
1161 }
1162
1163 pressedEvent = null;
1164 repostEvent(e);
1165 dispatchComponent = null;
1166 setValueIsAdjusting(false);
1167 }
1168
1169 private void mouseReleasedDND(MouseEvent e) {
1170 MouseEvent me = DragRecognitionSupport.mouseReleased(e);
1171 if (me != null) {
1172 SwingUtilities2.adjustFocus(table);
1173 if (!dragPressDidSelection) {
1174 adjustSelection(me);
1175 }
1176 }
1177
1178 if (!dragStarted) {
1179 if (isFileList) {
1180 maybeStartTimer();
1181 return;
1182 }
1183
1184 Point p = e.getPoint();
1185
1186 if (pressedEvent != null &&
1187 table.rowAtPoint(p) == pressedRow &&
1188 table.columnAtPoint(p) == pressedCol &&
1189 table.editCellAt(pressedRow, pressedCol, pressedEvent)) {
1190
1191 setDispatchComponent(pressedEvent);
1192 repostEvent(pressedEvent);
1193
1194 // This may appear completely odd, but must be done for backward
1195 // compatibility reasons. Developers have been known to rely on
1196 // a call to shouldSelectCell after editing has begun.
1197 CellEditor ce = table.getCellEditor();
1198 if (ce != null) {
1199 ce.shouldSelectCell(pressedEvent);
1200 }
1201 }
1202 }
1203 }
1204
1205 public void mouseEntered(MouseEvent e) {}
1206
1207 public void mouseExited(MouseEvent e) {}
1208
1209 public void mouseMoved(MouseEvent e) {}
1210
1211 public void dragStarting(MouseEvent me) {
1212 dragStarted = true;
1213
1214 if (BasicGraphicsUtils.isMenuShortcutKeyDown(me) && isFileList) {
1215 table.getSelectionModel().addSelectionInterval(pressedRow,
1216 pressedRow);
1217 table.getColumnModel().getSelectionModel().
1218 addSelectionInterval(pressedCol, pressedCol);
1219 }
1220
1221 pressedEvent = null;
1222 }
1223
1224 public void mouseDragged(MouseEvent e) {
1225 if (SwingUtilities2.shouldIgnore(e, table)) {
1226 return;
1227 }
1228
1229 if (table.getDragEnabled() &&
1230 (DragRecognitionSupport.mouseDragged(e, this) || dragStarted)) {
1231
1232 return;
1233 }
1234
1235 repostEvent(e);
1236
1237 // Check isFileList:
1238 // Until we support drag-selection, dragging should not change
1239 // the selection (act like single-select).
1240 if (isFileList || table.isEditing()) {
1241 return;
1242 }
1243
1244 Point p = e.getPoint();
1245 int row = table.rowAtPoint(p);
1246 int column = table.columnAtPoint(p);
1247 // The autoscroller can generate drag events outside the
1248 // table's range.
1249 if ((column == -1) || (row == -1)) {
1250 return;
1251 }
1252
1253 table.changeSelection(row, column,
1254 BasicGraphicsUtils.isMenuShortcutKeyDown(e), 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 sbg = UIManager.getColor("Table.selectionBackground");
1418 table.setSelectionBackground(sbg != null ? sbg : UIManager.getColor("textHighlight"));
1419 }
1420
1421 Color sfg = table.getSelectionForeground();
1422 if (sfg == null || sfg instanceof UIResource) {
1423 sfg = UIManager.getColor("Table.selectionForeground");
1424 table.setSelectionForeground(sfg != null ? sfg : UIManager.getColor("textHighlightText"));
1425 }
1426
1427 Color gridColor = table.getGridColor();
1428 if (gridColor == null || gridColor instanceof UIResource) {
1429 gridColor = UIManager.getColor("Table.gridColor");
1430 table.setGridColor(gridColor != null ? gridColor : Color.GRAY);
1431 }
1432
1433 // install the scrollpane border
1434 Container parent = table.getParent(); // should be viewport
1435 if (parent != null) {
1436 parent = parent.getParent(); // should be the scrollpane
1437 if (parent != null && parent instanceof JScrollPane) {
1438 LookAndFeel.installBorder((JScrollPane)parent, "Table.scrollPaneBorder");
1439 }
1440 }
1441
1442 isFileList = Boolean.TRUE.equals(table.getClientProperty("Table.isFileList"));
1443 }
1444
1445 private void installDefaults2() {
1446 TransferHandler th = table.getTransferHandler();
1447 if (th == null || th instanceof UIResource) {
1448 table.setTransferHandler(defaultTransferHandler);
1449 // default TransferHandler doesn't support drop
1450 // so we don't want drop handling
1451 if (table.getDropTarget() instanceof UIResource) {
1452 table.setDropTarget(null);
1453 }
1454 }
1455 }
1456
1457 /**
1458 * Attaches listeners to the JTable.
1459 */
1460 protected void installListeners() {
1461 focusListener = createFocusListener();
1462 keyListener = createKeyListener();
1463 mouseInputListener = createMouseInputListener();
1464
1465 table.addFocusListener(focusListener);
1466 table.addKeyListener(keyListener);
1467 table.addMouseListener(mouseInputListener);
1468 table.addMouseMotionListener(mouseInputListener);
1469 table.addPropertyChangeListener(getHandler());
1470 if (isFileList) {
1471 table.getSelectionModel().addListSelectionListener(getHandler());
1472 }
1473 }
1474
1475 /**
1476 * Register all keyboard actions on the JTable.
1477 */
1478 protected void installKeyboardActions() {
1479 LazyActionMap.installLazyActionMap(table, BasicTableUI.class,
1480 "Table.actionMap");
1481
1482 InputMap inputMap = getInputMap(JComponent.
1483 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
1484 SwingUtilities.replaceUIInputMap(table,
1485 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
1486 inputMap);
1487 }
1488
1489 InputMap getInputMap(int condition) {
1490 if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
1491 InputMap keyMap =
1492 (InputMap)DefaultLookup.get(table, this,
1493 "Table.ancestorInputMap");
1494 InputMap rtlKeyMap;
1495
1496 if (table.getComponentOrientation().isLeftToRight() ||
1497 ((rtlKeyMap = (InputMap)DefaultLookup.get(table, this,
1498 "Table.ancestorInputMap.RightToLeft")) == null)) {
1499 return keyMap;
1500 } else {
1501 rtlKeyMap.setParent(keyMap);
1502 return rtlKeyMap;
1503 }
1504 }
1505 return null;
1506 }
1507
1508 static void loadActionMap(LazyActionMap map) {
1509 // IMPORTANT: There is a very close coupling between the parameters
1510 // passed to the Actions constructor. Only certain parameter
1511 // combinations are supported. For example, the following Action would
1512 // not work as expected:
1513 // new Actions(Actions.NEXT_ROW_CELL, 1, 4, false, true)
1514 // Actions which move within the selection only (having a true
1515 // inSelection parameter) require that one of dx or dy be
1516 // zero and the other be -1 or 1. The point of this warning is
1517 // that you should be very careful about making sure a particular
1518 // combination of parameters is supported before changing or
1519 // adding anything here.
1520
1521 map.put(new Actions(Actions.NEXT_COLUMN, 1, 0,
1522 false, false));
1523 map.put(new Actions(Actions.NEXT_COLUMN_CHANGE_LEAD, 1, 0,
1524 false, false));
1525 map.put(new Actions(Actions.PREVIOUS_COLUMN, -1, 0,
1526 false, false));
1527 map.put(new Actions(Actions.PREVIOUS_COLUMN_CHANGE_LEAD, -1, 0,
1528 false, false));
1529 map.put(new Actions(Actions.NEXT_ROW, 0, 1,
1530 false, false));
1531 map.put(new Actions(Actions.NEXT_ROW_CHANGE_LEAD, 0, 1,
1532 false, false));
1533 map.put(new Actions(Actions.PREVIOUS_ROW, 0, -1,
1534 false, false));
1535 map.put(new Actions(Actions.PREVIOUS_ROW_CHANGE_LEAD, 0, -1,
1536 false, false));
1537 map.put(new Actions(Actions.NEXT_COLUMN_EXTEND_SELECTION,
1538 1, 0, true, false));
1539 map.put(new Actions(Actions.PREVIOUS_COLUMN_EXTEND_SELECTION,
1540 -1, 0, true, false));
1541 map.put(new Actions(Actions.NEXT_ROW_EXTEND_SELECTION,
1542 0, 1, true, false));
1543 map.put(new Actions(Actions.PREVIOUS_ROW_EXTEND_SELECTION,
1544 0, -1, true, false));
1545 map.put(new Actions(Actions.SCROLL_UP_CHANGE_SELECTION,
1546 false, false, true, false));
1547 map.put(new Actions(Actions.SCROLL_DOWN_CHANGE_SELECTION,
1548 false, true, true, false));
1549 map.put(new Actions(Actions.FIRST_COLUMN,
1550 false, false, false, true));
1551 map.put(new Actions(Actions.LAST_COLUMN,
1552 false, true, false, true));
1553
1554 map.put(new Actions(Actions.SCROLL_UP_EXTEND_SELECTION,
1555 true, false, true, false));
1556 map.put(new Actions(Actions.SCROLL_DOWN_EXTEND_SELECTION,
1557 true, true, true, false));
1558 map.put(new Actions(Actions.FIRST_COLUMN_EXTEND_SELECTION,
1559 true, false, false, true));
1560 map.put(new Actions(Actions.LAST_COLUMN_EXTEND_SELECTION,
1561 true, true, false, true));
1562
1563 map.put(new Actions(Actions.FIRST_ROW, false, false, true, true));
1564 map.put(new Actions(Actions.LAST_ROW, false, true, true, true));
1565
1566 map.put(new Actions(Actions.FIRST_ROW_EXTEND_SELECTION,
1567 true, false, true, true));
1568 map.put(new Actions(Actions.LAST_ROW_EXTEND_SELECTION,
1569 true, true, true, true));
1570
1571 map.put(new Actions(Actions.NEXT_COLUMN_CELL,
1572 1, 0, false, true));
1573 map.put(new Actions(Actions.PREVIOUS_COLUMN_CELL,
1574 -1, 0, false, true));
1575 map.put(new Actions(Actions.NEXT_ROW_CELL, 0, 1, false, true));
1576 map.put(new Actions(Actions.PREVIOUS_ROW_CELL,
1577 0, -1, false, true));
1578
1579 map.put(new Actions(Actions.SELECT_ALL));
1580 map.put(new Actions(Actions.CLEAR_SELECTION));
1581 map.put(new Actions(Actions.CANCEL_EDITING));
1582 map.put(new Actions(Actions.START_EDITING));
1583
1584 map.put(TransferHandler.getCutAction().getValue(Action.NAME),
1585 TransferHandler.getCutAction());
1586 map.put(TransferHandler.getCopyAction().getValue(Action.NAME),
1587 TransferHandler.getCopyAction());
1588 map.put(TransferHandler.getPasteAction().getValue(Action.NAME),
1589 TransferHandler.getPasteAction());
1590
1591 map.put(new Actions(Actions.SCROLL_LEFT_CHANGE_SELECTION,
1592 false, false, false, false));
1593 map.put(new Actions(Actions.SCROLL_RIGHT_CHANGE_SELECTION,
1594 false, true, false, false));
1595 map.put(new Actions(Actions.SCROLL_LEFT_EXTEND_SELECTION,
1596 true, false, false, false));
1597 map.put(new Actions(Actions.SCROLL_RIGHT_EXTEND_SELECTION,
1598 true, true, false, false));
1599
1600 map.put(new Actions(Actions.ADD_TO_SELECTION));
1601 map.put(new Actions(Actions.TOGGLE_AND_ANCHOR));
1602 map.put(new Actions(Actions.EXTEND_TO));
1603 map.put(new Actions(Actions.MOVE_SELECTION_TO));
1604 map.put(new Actions(Actions.FOCUS_HEADER));
1605 }
1606
1607// Uninstallation
1608
1609 public void uninstallUI(JComponent c) {
1610 uninstallDefaults();
1611 uninstallListeners();
1612 uninstallKeyboardActions();
1613
1614 table.remove(rendererPane);
1615 rendererPane = null;
1616 table = null;
1617 }
1618
1619 protected void uninstallDefaults() {
1620 if (table.getTransferHandler() instanceof UIResource) {
1621 table.setTransferHandler(null);
1622 }
1623 }
1624
1625 protected void uninstallListeners() {
1626 table.removeFocusListener(focusListener);
1627 table.removeKeyListener(keyListener);
1628 table.removeMouseListener(mouseInputListener);
1629 table.removeMouseMotionListener(mouseInputListener);
1630 table.removePropertyChangeListener(getHandler());
1631 if (isFileList) {
1632 table.getSelectionModel().removeListSelectionListener(getHandler());
1633 }
1634
1635 focusListener = null;
1636 keyListener = null;
1637 mouseInputListener = null;
1638 handler = null;
1639 }
1640
1641 protected void uninstallKeyboardActions() {
1642 SwingUtilities.replaceUIInputMap(table, JComponent.
1643 WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
1644 SwingUtilities.replaceUIActionMap(table, null);
1645 }
1646
1647 /**
1648 * Returns the baseline.
1649 *
1650 * @throws NullPointerException {@inheritDoc}
1651 * @throws IllegalArgumentException {@inheritDoc}
1652 * @see javax.swing.JComponent#getBaseline(int, int)
1653 * @since 1.6
1654 */
1655 public int getBaseline(JComponent c, int width, int height) {
1656 super.getBaseline(c, width, height);
1657 UIDefaults lafDefaults = UIManager.getLookAndFeelDefaults();
1658 Component renderer = (Component)lafDefaults.get(
1659 BASELINE_COMPONENT_KEY);
1660 if (renderer == null) {
1661 DefaultTableCellRenderer tcr = new DefaultTableCellRenderer();
1662 renderer = tcr.getTableCellRendererComponent(
1663 table, "a", false, false, -1, -1);
1664 lafDefaults.put(BASELINE_COMPONENT_KEY, renderer);
1665 }
1666 renderer.setFont(table.getFont());
1667 int rowMargin = table.getRowMargin();
1668 return renderer.getBaseline(Integer.MAX_VALUE, table.getRowHeight() -
1669 rowMargin) + rowMargin / 2;
1670 }
1671
1672 /**
1673 * Returns an enum indicating how the baseline of the component
1674 * changes as the size changes.
1675 *
1676 * @throws NullPointerException {@inheritDoc}
1677 * @see javax.swing.JComponent#getBaseline(int, int)
1678 * @since 1.6
1679 */
1680 public Component.BaselineResizeBehavior getBaselineResizeBehavior(
1681 JComponent c) {
1682 super.getBaselineResizeBehavior(c);
1683 return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
1684 }
1685
1686//
1687// Size Methods
1688//
1689
1690 private Dimension createTableSize(long width) {
1691 int height = 0;
1692 int rowCount = table.getRowCount();
1693 if (rowCount > 0 && table.getColumnCount() > 0) {
1694 Rectangle r = table.getCellRect(rowCount-1, 0, true);
1695 height = r.y + r.height;
1696 }
1697 // Width is always positive. The call to abs() is a workaround for
1698 // a bug in the 1.1.6 JIT on Windows.
1699 long tmp = Math.abs(width);
1700 if (tmp > Integer.MAX_VALUE) {
1701 tmp = Integer.MAX_VALUE;
1702 }
1703 return new Dimension((int)tmp, height);
1704 }
1705
1706 /**
1707 * Return the minimum size of the table. The minimum height is the
1708 * row height times the number of rows.
1709 * The minimum width is the sum of the minimum widths of each column.
1710 */
1711 public Dimension getMinimumSize(JComponent c) {
1712 long width = 0;
1713 Enumeration enumeration = table.getColumnModel().getColumns();
1714 while (enumeration.hasMoreElements()) {
1715 TableColumn aColumn = (TableColumn)enumeration.nextElement();
1716 width = width + aColumn.getMinWidth();
1717 }
1718 return createTableSize(width);
1719 }
1720
1721 /**
1722 * Return the preferred size of the table. The preferred height is the
1723 * row height times the number of rows.
1724 * The preferred width is the sum of the preferred widths of each column.
1725 */
1726 public Dimension getPreferredSize(JComponent c) {
1727 long width = 0;
1728 Enumeration enumeration = table.getColumnModel().getColumns();
1729 while (enumeration.hasMoreElements()) {
1730 TableColumn aColumn = (TableColumn)enumeration.nextElement();
1731 width = width + aColumn.getPreferredWidth();
1732 }
1733 return createTableSize(width);
1734 }
1735
1736 /**
1737 * Return the maximum size of the table. The maximum height is the
1738 * row heighttimes the number of rows.
1739 * The maximum width is the sum of the maximum widths of each column.
1740 */
1741 public Dimension getMaximumSize(JComponent c) {
1742 long width = 0;
1743 Enumeration enumeration = table.getColumnModel().getColumns();
1744 while (enumeration.hasMoreElements()) {
1745 TableColumn aColumn = (TableColumn)enumeration.nextElement();
1746 width = width + aColumn.getMaxWidth();
1747 }
1748 return createTableSize(width);
1749 }
1750
1751//
1752// Paint methods and support
1753//
1754
1755 /** Paint a representation of the <code>table</code> instance
1756 * that was set in installUI().
1757 */
1758 public void paint(Graphics g, JComponent c) {
1759 Rectangle clip = g.getClipBounds();
1760
1761 Rectangle bounds = table.getBounds();
1762 // account for the fact that the graphics has already been translated
1763 // into the table's bounds
1764 bounds.x = bounds.y = 0;
1765
1766 if (table.getRowCount() <= 0 || table.getColumnCount() <= 0 ||
1767 // this check prevents us from painting the entire table
1768 // when the clip doesn't intersect our bounds at all
1769 !bounds.intersects(clip)) {
1770
1771 paintDropLines(g);
1772 return;
1773 }
1774
1775 boolean ltr = table.getComponentOrientation().isLeftToRight();
1776
1777 Point upperLeft = clip.getLocation();
1778 Point lowerRight = new Point(clip.x + clip.width - 1,
1779 clip.y + clip.height - 1);
1780
1781 int rMin = table.rowAtPoint(upperLeft);
1782 int rMax = table.rowAtPoint(lowerRight);
1783 // This should never happen (as long as our bounds intersect the clip,
1784 // which is why we bail above if that is the case).
1785 if (rMin == -1) {
1786 rMin = 0;
1787 }
1788 // If the table does not have enough rows to fill the view we'll get -1.
1789 // (We could also get -1 if our bounds don't intersect the clip,
1790 // which is why we bail above if that is the case).
1791 // Replace this with the index of the last row.
1792 if (rMax == -1) {
1793 rMax = table.getRowCount()-1;
1794 }
1795
1796 int cMin = table.columnAtPoint(ltr ? upperLeft : lowerRight);
1797 int cMax = table.columnAtPoint(ltr ? lowerRight : upperLeft);
1798 // This should never happen.
1799 if (cMin == -1) {
1800 cMin = 0;
1801 }
1802 // If the table does not have enough columns to fill the view we'll get -1.
1803 // Replace this with the index of the last column.
1804 if (cMax == -1) {
1805 cMax = table.getColumnCount()-1;
1806 }
1807
1808 // Paint the grid.
1809 paintGrid(g, rMin, rMax, cMin, cMax);
1810
1811 // Paint the cells.
1812 paintCells(g, rMin, rMax, cMin, cMax);
1813
1814 paintDropLines(g);
1815 }
1816
1817 private void paintDropLines(Graphics g) {
1818 JTable.DropLocation loc = table.getDropLocation();
1819 if (loc == null) {
1820 return;
1821 }
1822
1823 Color color = UIManager.getColor("Table.dropLineColor");
1824 Color shortColor = UIManager.getColor("Table.dropLineShortColor");
1825 if (color == null && shortColor == null) {
1826 return;
1827 }
1828
1829 Rectangle rect;
1830
1831 rect = getHDropLineRect(loc);
1832 if (rect != null) {
1833 int x = rect.x;
1834 int w = rect.width;
1835 if (color != null) {
1836 extendRect(rect, true);
1837 g.setColor(color);
1838 g.fillRect(rect.x, rect.y, rect.width, rect.height);
1839 }
1840 if (!loc.isInsertColumn() && shortColor != null) {
1841 g.setColor(shortColor);
1842 g.fillRect(x, rect.y, w, rect.height);
1843 }
1844 }
1845
1846 rect = getVDropLineRect(loc);
1847 if (rect != null) {
1848 int y = rect.y;
1849 int h = rect.height;
1850 if (color != null) {
1851 extendRect(rect, false);
1852 g.setColor(color);
1853 g.fillRect(rect.x, rect.y, rect.width, rect.height);
1854 }
1855 if (!loc.isInsertRow() && shortColor != null) {
1856 g.setColor(shortColor);
1857 g.fillRect(rect.x, y, rect.width, h);
1858 }
1859 }
1860 }
1861
1862 private Rectangle getHDropLineRect(JTable.DropLocation loc) {
1863 if (!loc.isInsertRow()) {
1864 return null;
1865 }
1866
1867 int row = loc.getRow();
1868 int col = loc.getColumn();
1869 if (col >= table.getColumnCount()) {
1870 col--;
1871 }
1872
1873 Rectangle rect = table.getCellRect(row, col, true);
1874
1875 if (row >= table.getRowCount()) {
1876 row--;
1877 Rectangle prevRect = table.getCellRect(row, col, true);
1878 rect.y = prevRect.y + prevRect.height;
1879 }
1880
1881 if (rect.y == 0) {
1882 rect.y = -1;
1883 } else {
1884 rect.y -= 2;
1885 }
1886
1887 rect.height = 3;
1888
1889 return rect;
1890 }
1891
1892 private Rectangle getVDropLineRect(JTable.DropLocation loc) {
1893 if (!loc.isInsertColumn()) {
1894 return null;
1895 }
1896
1897 boolean ltr = table.getComponentOrientation().isLeftToRight();
1898 int col = loc.getColumn();
1899 Rectangle rect = table.getCellRect(loc.getRow(), col, true);
1900
1901 if (col >= table.getColumnCount()) {
1902 col--;
1903 rect = table.getCellRect(loc.getRow(), col, true);
1904 if (ltr) {
1905 rect.x = rect.x + rect.width;
1906 }
1907 } else if (!ltr) {
1908 rect.x = rect.x + rect.width;
1909 }
1910
1911 if (rect.x == 0) {
1912 rect.x = -1;
1913 } else {
1914 rect.x -= 2;
1915 }
1916
1917 rect.width = 3;
1918
1919 return rect;
1920 }
1921
1922 private Rectangle extendRect(Rectangle rect, boolean horizontal) {
1923 if (rect == null) {
1924 return rect;
1925 }
1926
1927 if (horizontal) {
1928 rect.x = 0;
1929 rect.width = table.getWidth();
1930 } else {
1931 rect.y = 0;
1932
1933 if (table.getRowCount() != 0) {
1934 Rectangle lastRect = table.getCellRect(table.getRowCount() - 1, 0, true);
1935 rect.height = lastRect.y + lastRect.height;
1936 } else {
1937 rect.height = table.getHeight();
1938 }
1939 }
1940
1941 return rect;
1942 }
1943
1944 /*
1945 * Paints the grid lines within <I>aRect</I>, using the grid
1946 * color set with <I>setGridColor</I>. Paints vertical lines
1947 * if <code>getShowVerticalLines()</code> returns true and paints
1948 * horizontal lines if <code>getShowHorizontalLines()</code>
1949 * returns true.
1950 */
1951 private void paintGrid(Graphics g, int rMin, int rMax, int cMin, int cMax) {
1952 g.setColor(table.getGridColor());
1953
1954 Rectangle minCell = table.getCellRect(rMin, cMin, true);
1955 Rectangle maxCell = table.getCellRect(rMax, cMax, true);
1956 Rectangle damagedArea = minCell.union( maxCell );
1957
1958 if (table.getShowHorizontalLines()) {
1959 int tableWidth = damagedArea.x + damagedArea.width;
1960 int y = damagedArea.y;
1961 for (int row = rMin; row <= rMax; row++) {
1962 y += table.getRowHeight(row);
1963 g.drawLine(damagedArea.x, y - 1, tableWidth - 1, y - 1);
1964 }
1965 }
1966 if (table.getShowVerticalLines()) {
1967 TableColumnModel cm = table.getColumnModel();
1968 int tableHeight = damagedArea.y + damagedArea.height;
1969 int x;
1970 if (table.getComponentOrientation().isLeftToRight()) {
1971 x = damagedArea.x;
1972 for (int column = cMin; column <= cMax; column++) {
1973 int w = cm.getColumn(column).getWidth();
1974 x += w;
1975 g.drawLine(x - 1, 0, x - 1, tableHeight - 1);
1976 }
1977 } else {
1978 x = damagedArea.x;
1979 for (int column = cMax; column >= cMin; column--) {
1980 int w = cm.getColumn(column).getWidth();
1981 x += w;
1982 g.drawLine(x - 1, 0, x - 1, tableHeight - 1);
1983 }
1984 }
1985 }
1986 }
1987
1988 private int viewIndexForColumn(TableColumn aColumn) {
1989 TableColumnModel cm = table.getColumnModel();
1990 for (int column = 0; column < cm.getColumnCount(); column++) {
1991 if (cm.getColumn(column) == aColumn) {
1992 return column;
1993 }
1994 }
1995 return -1;
1996 }
1997
1998 private void paintCells(Graphics g, int rMin, int rMax, int cMin, int cMax) {
1999 JTableHeader header = table.getTableHeader();
2000 TableColumn draggedColumn = (header == null) ? null : header.getDraggedColumn();
2001
2002 TableColumnModel cm = table.getColumnModel();
2003 int columnMargin = cm.getColumnMargin();
2004
2005 Rectangle cellRect;
2006 TableColumn aColumn;
2007 int columnWidth;
2008 if (table.getComponentOrientation().isLeftToRight()) {
2009 for(int row = rMin; row <= rMax; row++) {
2010 cellRect = table.getCellRect(row, cMin, false);
2011 for(int column = cMin; column <= cMax; column++) {
2012 aColumn = cm.getColumn(column);
2013 columnWidth = aColumn.getWidth();
2014 cellRect.width = columnWidth - columnMargin;
2015 if (aColumn != draggedColumn) {
2016 paintCell(g, cellRect, row, column);
2017 }
2018 cellRect.x += columnWidth;
2019 }
2020 }
2021 } else {
2022 for(int row = rMin; row <= rMax; row++) {
2023 cellRect = table.getCellRect(row, cMin, false);
2024 aColumn = cm.getColumn(cMin);
2025 if (aColumn != draggedColumn) {
2026 columnWidth = aColumn.getWidth();
2027 cellRect.width = columnWidth - columnMargin;
2028 paintCell(g, cellRect, row, cMin);
2029 }
2030 for(int column = cMin+1; column <= cMax; column++) {
2031 aColumn = cm.getColumn(column);
2032 columnWidth = aColumn.getWidth();
2033 cellRect.width = columnWidth - columnMargin;
2034 cellRect.x -= columnWidth;
2035 if (aColumn != draggedColumn) {
2036 paintCell(g, cellRect, row, column);
2037 }
2038 }
2039 }
2040 }
2041
2042 // Paint the dragged column if we are dragging.
2043 if (draggedColumn != null) {
2044 paintDraggedArea(g, rMin, rMax, draggedColumn, header.getDraggedDistance());
2045 }
2046
2047 // Remove any renderers that may be left in the rendererPane.
2048 rendererPane.removeAll();
2049 }
2050
2051 private void paintDraggedArea(Graphics g, int rMin, int rMax, TableColumn draggedColumn, int distance) {
2052 int draggedColumnIndex = viewIndexForColumn(draggedColumn);
2053
2054 Rectangle minCell = table.getCellRect(rMin, draggedColumnIndex, true);
2055 Rectangle maxCell = table.getCellRect(rMax, draggedColumnIndex, true);
2056
2057 Rectangle vacatedColumnRect = minCell.union(maxCell);
2058
2059 // Paint a gray well in place of the moving column.
2060 g.setColor(table.getParent().getBackground());
2061 g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
2062 vacatedColumnRect.width, vacatedColumnRect.height);
2063
2064 // Move to the where the cell has been dragged.
2065 vacatedColumnRect.x += distance;
2066
2067 // Fill the background.
2068 g.setColor(table.getBackground());
2069 g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
2070 vacatedColumnRect.width, vacatedColumnRect.height);
2071
2072 // Paint the vertical grid lines if necessary.
2073 if (table.getShowVerticalLines()) {
2074 g.setColor(table.getGridColor());
2075 int x1 = vacatedColumnRect.x;
2076 int y1 = vacatedColumnRect.y;
2077 int x2 = x1 + vacatedColumnRect.width - 1;
2078 int y2 = y1 + vacatedColumnRect.height - 1;
2079 // Left
2080 g.drawLine(x1-1, y1, x1-1, y2);
2081 // Right
2082 g.drawLine(x2, y1, x2, y2);
2083 }
2084
2085 for(int row = rMin; row <= rMax; row++) {
2086 // Render the cell value
2087 Rectangle r = table.getCellRect(row, draggedColumnIndex, false);
2088 r.x += distance;
2089 paintCell(g, r, row, draggedColumnIndex);
2090
2091 // Paint the (lower) horizontal grid line if necessary.
2092 if (table.getShowHorizontalLines()) {
2093 g.setColor(table.getGridColor());
2094 Rectangle rcr = table.getCellRect(row, draggedColumnIndex, true);
2095 rcr.x += distance;
2096 int x1 = rcr.x;
2097 int y1 = rcr.y;
2098 int x2 = x1 + rcr.width - 1;
2099 int y2 = y1 + rcr.height - 1;
2100 g.drawLine(x1, y2, x2, y2);
2101 }
2102 }
2103 }
2104
2105 private void paintCell(Graphics g, Rectangle cellRect, int row, int column) {
2106 if (table.isEditing() && table.getEditingRow()==row &&
2107 table.getEditingColumn()==column) {
2108 Component component = table.getEditorComponent();
2109 component.setBounds(cellRect);
2110 component.validate();
2111 }
2112 else {
2113 TableCellRenderer renderer = table.getCellRenderer(row, column);
2114 Component component = table.prepareRenderer(renderer, row, column);
2115 rendererPane.paintComponent(g, component, table, cellRect.x, cellRect.y,
2116 cellRect.width, cellRect.height, true);
2117 }
2118 }
2119
2120 private static int getAdjustedLead(JTable table,
2121 boolean row,
2122 ListSelectionModel model) {
2123
2124 int index = model.getLeadSelectionIndex();
2125 int compare = row ? table.getRowCount() : table.getColumnCount();
2126 return index < compare ? index : -1;
2127 }
2128
2129 private static int getAdjustedLead(JTable table, boolean row) {
2130 return row ? getAdjustedLead(table, row, table.getSelectionModel())
2131 : getAdjustedLead(table, row, table.getColumnModel().getSelectionModel());
2132 }
2133
2134
2135 private static final TransferHandler defaultTransferHandler = new TableTransferHandler();
2136
2137 static class TableTransferHandler extends TransferHandler implements UIResource {
2138
2139 /**
2140 * Create a Transferable to use as the source for a data transfer.
2141 *
2142 * @param c The component holding the data to be transfered. This
2143 * argument is provided to enable sharing of TransferHandlers by
2144 * multiple components.
2145 * @return The representation of the data to be transfered.
2146 *
2147 */
2148 protected Transferable createTransferable(JComponent c) {
2149 if (c instanceof JTable) {
2150 JTable table = (JTable) c;
2151 int[] rows;
2152 int[] cols;
2153
2154 if (!table.getRowSelectionAllowed() && !table.getColumnSelectionAllowed()) {
2155 return null;
2156 }
2157
2158 if (!table.getRowSelectionAllowed()) {
2159 int rowCount = table.getRowCount();
2160
2161 rows = new int[rowCount];
2162 for (int counter = 0; counter < rowCount; counter++) {
2163 rows[counter] = counter;
2164 }
2165 } else {
2166 rows = table.getSelectedRows();
2167 }
2168
2169 if (!table.getColumnSelectionAllowed()) {
2170 int colCount = table.getColumnCount();
2171
2172 cols = new int[colCount];
2173 for (int counter = 0; counter < colCount; counter++) {
2174 cols[counter] = counter;
2175 }
2176 } else {
2177 cols = table.getSelectedColumns();
2178 }
2179
2180 if (rows == null || cols == null || rows.length == 0 || cols.length == 0) {
2181 return null;
2182 }
2183
2184 StringBuffer plainBuf = new StringBuffer();
2185 StringBuffer htmlBuf = new StringBuffer();
2186
2187 htmlBuf.append("<html>\n<body>\n<table>\n");
2188
2189 for (int row = 0; row < rows.length; row++) {
2190 htmlBuf.append("<tr>\n");
2191 for (int col = 0; col < cols.length; col++) {
2192 Object obj = table.getValueAt(rows[row], cols[col]);
2193 String val = ((obj == null) ? "" : obj.toString());
2194 plainBuf.append(val + "\t");
2195 htmlBuf.append(" <td>" + val + "</td>\n");
2196 }
2197 // we want a newline at the end of each line and not a tab
2198 plainBuf.deleteCharAt(plainBuf.length() - 1).append("\n");
2199 htmlBuf.append("</tr>\n");
2200 }
2201
2202 // remove the last newline
2203 plainBuf.deleteCharAt(plainBuf.length() - 1);
2204 htmlBuf.append("</table>\n</body>\n</html>");
2205
2206 return new BasicTransferable(plainBuf.toString(), htmlBuf.toString());
2207 }
2208
2209 return null;
2210 }
2211
2212 public int getSourceActions(JComponent c) {
2213 return COPY;
2214 }
2215
2216 }
2217} // End of Class BasicTableUI