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