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+
26+package javax.swing.plaf.basic;
27+
28+import java.awt.*;
29+import java.awt.datatransfer.*;
30+import java.awt.dnd.*;
31+import java.awt.event.*;
32+import java.util.Enumeration;
33+import java.util.EventObject;
34+import java.util.Hashtable;
35+import java.util.TooManyListenersException;
36+import javax.swing.*;
37+import javax.swing.event.*;
38+import javax.swing.plaf.*;
39+import javax.swing.text.*;
40+import javax.swing.table.*;
41+import javax.swing.plaf.basic.DragRecognitionSupport.BeforeDrag;
42+import sun.swing.SwingUtilities2;
43+
44+
45+import java.beans.PropertyChangeEvent;
46+import java.beans.PropertyChangeListener;
47+
48+import sun.swing.DefaultLookup;
49+import sun.swing.UIAction;
50+
51+/**
52+ * BasicTableUI implementation
53+ *
54+ * @author Philip Milne
55+ * @author Shannon Hickey (drag and drop)
56+ */
57+public 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