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