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