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