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