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