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