0001 /*
0002 * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved.
0003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004 *
0005 * This code is free software; you can redistribute it and/or modify it
0006 * under the terms of the GNU General Public License version 2 only, as
0007 * published by the Free Software Foundation. Sun designates this
0008 * particular file as subject to the "Classpath" exception as provided
0009 * by Sun in the LICENSE file that accompanied this code.
0010 *
0011 * This code is distributed in the hope that it will be useful, but WITHOUT
0012 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014 * version 2 for more details (a copy is included in the LICENSE file that
0015 * accompanied this code).
0016 *
0017 * You should have received a copy of the GNU General Public License version
0018 * 2 along with this work; if not, write to the Free Software Foundation,
0019 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020 *
0021 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022 * CA 95054 USA or visit www.sun.com if you need additional information or
0023 * have any questions.
0024 */
0025
0026 package javax.swing.plaf.basic;
0027
0028 import java.awt.*;
0029 import java.awt.datatransfer.*;
0030 import java.awt.dnd.*;
0031 import java.awt.event.*;
0032 import java.util.Enumeration;
0033 import java.util.EventObject;
0034 import java.util.Hashtable;
0035 import java.util.TooManyListenersException;
0036 import javax.swing.*;
0037 import javax.swing.event.*;
0038 import javax.swing.plaf.*;
0039 import javax.swing.text.*;
0040 import javax.swing.table.*;
0041 import javax.swing.plaf.basic.DragRecognitionSupport.BeforeDrag;
0042 import sun.swing.SwingUtilities2;
0043
0044 import java.beans.PropertyChangeEvent;
0045 import java.beans.PropertyChangeListener;
0046
0047 import sun.swing.DefaultLookup;
0048 import sun.swing.UIAction;
0049
0050 /**
0051 * BasicTableUI implementation
0052 *
0053 * @version 1.165 05/05/07
0054 * @author Philip Milne
0055 * @author Shannon Hickey (drag and drop)
0056 */
0057 public class BasicTableUI extends TableUI {
0058 private static final StringBuilder BASELINE_COMPONENT_KEY = new StringBuilder(
0059 "Table.baselineComponent");
0060
0061 //
0062 // Instance Variables
0063 //
0064
0065 // The JTable that is delegating the painting to this UI.
0066 protected JTable table;
0067 protected CellRendererPane rendererPane;
0068
0069 // Listeners that are attached to the JTable
0070 protected KeyListener keyListener;
0071 protected FocusListener focusListener;
0072 protected MouseInputListener mouseInputListener;
0073
0074 private Handler handler;
0075
0076 /**
0077 * Local cache of Table's client property "Table.isFileList"
0078 */
0079 private boolean isFileList = false;
0080
0081 //
0082 // Helper class for keyboard actions
0083 //
0084
0085 private static class Actions extends UIAction {
0086 private static final String CANCEL_EDITING = "cancel";
0087 private static final String SELECT_ALL = "selectAll";
0088 private static final String CLEAR_SELECTION = "clearSelection";
0089 private static final String START_EDITING = "startEditing";
0090
0091 private static final String NEXT_ROW = "selectNextRow";
0092 private static final String NEXT_ROW_CELL = "selectNextRowCell";
0093 private static final String NEXT_ROW_EXTEND_SELECTION = "selectNextRowExtendSelection";
0094 private static final String NEXT_ROW_CHANGE_LEAD = "selectNextRowChangeLead";
0095 private static final String PREVIOUS_ROW = "selectPreviousRow";
0096 private static final String PREVIOUS_ROW_CELL = "selectPreviousRowCell";
0097 private static final String PREVIOUS_ROW_EXTEND_SELECTION = "selectPreviousRowExtendSelection";
0098 private static final String PREVIOUS_ROW_CHANGE_LEAD = "selectPreviousRowChangeLead";
0099
0100 private static final String NEXT_COLUMN = "selectNextColumn";
0101 private static final String NEXT_COLUMN_CELL = "selectNextColumnCell";
0102 private static final String NEXT_COLUMN_EXTEND_SELECTION = "selectNextColumnExtendSelection";
0103 private static final String NEXT_COLUMN_CHANGE_LEAD = "selectNextColumnChangeLead";
0104 private static final String PREVIOUS_COLUMN = "selectPreviousColumn";
0105 private static final String PREVIOUS_COLUMN_CELL = "selectPreviousColumnCell";
0106 private static final String PREVIOUS_COLUMN_EXTEND_SELECTION = "selectPreviousColumnExtendSelection";
0107 private static final String PREVIOUS_COLUMN_CHANGE_LEAD = "selectPreviousColumnChangeLead";
0108
0109 private static final String SCROLL_LEFT_CHANGE_SELECTION = "scrollLeftChangeSelection";
0110 private static final String SCROLL_LEFT_EXTEND_SELECTION = "scrollLeftExtendSelection";
0111 private static final String SCROLL_RIGHT_CHANGE_SELECTION = "scrollRightChangeSelection";
0112 private static final String SCROLL_RIGHT_EXTEND_SELECTION = "scrollRightExtendSelection";
0113
0114 private static final String SCROLL_UP_CHANGE_SELECTION = "scrollUpChangeSelection";
0115 private static final String SCROLL_UP_EXTEND_SELECTION = "scrollUpExtendSelection";
0116 private static final String SCROLL_DOWN_CHANGE_SELECTION = "scrollDownChangeSelection";
0117 private static final String SCROLL_DOWN_EXTEND_SELECTION = "scrollDownExtendSelection";
0118
0119 private static final String FIRST_COLUMN = "selectFirstColumn";
0120 private static final String FIRST_COLUMN_EXTEND_SELECTION = "selectFirstColumnExtendSelection";
0121 private static final String LAST_COLUMN = "selectLastColumn";
0122 private static final String LAST_COLUMN_EXTEND_SELECTION = "selectLastColumnExtendSelection";
0123
0124 private static final String FIRST_ROW = "selectFirstRow";
0125 private static final String FIRST_ROW_EXTEND_SELECTION = "selectFirstRowExtendSelection";
0126 private static final String LAST_ROW = "selectLastRow";
0127 private static final String LAST_ROW_EXTEND_SELECTION = "selectLastRowExtendSelection";
0128
0129 // add the lead item to the selection without changing lead or anchor
0130 private static final String ADD_TO_SELECTION = "addToSelection";
0131
0132 // toggle the selected state of the lead item and move the anchor to it
0133 private static final String TOGGLE_AND_ANCHOR = "toggleAndAnchor";
0134
0135 // extend the selection to the lead item
0136 private static final String EXTEND_TO = "extendTo";
0137
0138 // move the anchor to the lead and ensure only that item is selected
0139 private static final String MOVE_SELECTION_TO = "moveSelectionTo";
0140
0141 // give focus to the JTableHeader, if one exists
0142 private static final String FOCUS_HEADER = "focusHeader";
0143
0144 protected int dx;
0145 protected int dy;
0146 protected boolean extend;
0147 protected boolean inSelection;
0148
0149 // horizontally, forwards always means right,
0150 // regardless of component orientation
0151 protected boolean forwards;
0152 protected boolean vertically;
0153 protected boolean toLimit;
0154
0155 protected int leadRow;
0156 protected int leadColumn;
0157
0158 Actions(String name) {
0159 super (name);
0160 }
0161
0162 Actions(String name, int dx, int dy, boolean extend,
0163 boolean inSelection) {
0164 super (name);
0165
0166 // Actions spcifying true for "inSelection" are
0167 // fairly sensitive to bad parameter values. They require
0168 // that one of dx and dy be 0 and the other be -1 or 1.
0169 // Bogus parameter values could cause an infinite loop.
0170 // To prevent any problems we massage the params here
0171 // and complain if we get something we can't deal with.
0172 if (inSelection) {
0173 this .inSelection = true;
0174
0175 // look at the sign of dx and dy only
0176 dx = sign(dx);
0177 dy = sign(dy);
0178
0179 // make sure one is zero, but not both
0180 assert (dx == 0 || dy == 0) && !(dx == 0 && dy == 0);
0181 }
0182
0183 this .dx = dx;
0184 this .dy = dy;
0185 this .extend = extend;
0186 }
0187
0188 Actions(String name, boolean extend, boolean forwards,
0189 boolean vertically, boolean toLimit) {
0190 this (name, 0, 0, extend, false);
0191 this .forwards = forwards;
0192 this .vertically = vertically;
0193 this .toLimit = toLimit;
0194 }
0195
0196 private static int clipToRange(int i, int a, int b) {
0197 return Math.min(Math.max(i, a), b - 1);
0198 }
0199
0200 private void moveWithinTableRange(JTable table, int dx, int dy) {
0201 leadRow = clipToRange(leadRow + dy, 0, table.getRowCount());
0202 leadColumn = clipToRange(leadColumn + dx, 0, table
0203 .getColumnCount());
0204 }
0205
0206 private static int sign(int num) {
0207 return (num < 0) ? -1 : ((num == 0) ? 0 : 1);
0208 }
0209
0210 /**
0211 * Called to move within the selected range of the given JTable.
0212 * This method uses the table's notion of selection, which is
0213 * important to allow the user to navigate between items visually
0214 * selected on screen. This notion may or may not be the same as
0215 * what could be determined by directly querying the selection models.
0216 * It depends on certain table properties (such as whether or not
0217 * row or column selection is allowed). When performing modifications,
0218 * it is recommended that caution be taken in order to preserve
0219 * the intent of this method, especially when deciding whether to
0220 * query the selection models or interact with JTable directly.
0221 */
0222 private boolean moveWithinSelectedRange(JTable table, int dx,
0223 int dy, ListSelectionModel rsm, ListSelectionModel csm) {
0224
0225 // Note: The Actions constructor ensures that only one of
0226 // dx and dy is 0, and the other is either -1 or 1
0227
0228 // find out how many items the table is showing as selected
0229 // and the range of items to navigate through
0230 int totalCount;
0231 int minX, maxX, minY, maxY;
0232
0233 boolean rs = table.getRowSelectionAllowed();
0234 boolean cs = table.getColumnSelectionAllowed();
0235
0236 // both column and row selection
0237 if (rs && cs) {
0238 totalCount = table.getSelectedRowCount()
0239 * table.getSelectedColumnCount();
0240 minX = csm.getMinSelectionIndex();
0241 maxX = csm.getMaxSelectionIndex();
0242 minY = rsm.getMinSelectionIndex();
0243 maxY = rsm.getMaxSelectionIndex();
0244 // row selection only
0245 } else if (rs) {
0246 totalCount = table.getSelectedRowCount();
0247 minX = 0;
0248 maxX = table.getColumnCount() - 1;
0249 minY = rsm.getMinSelectionIndex();
0250 maxY = rsm.getMaxSelectionIndex();
0251 // column selection only
0252 } else if (cs) {
0253 totalCount = table.getSelectedColumnCount();
0254 minX = csm.getMinSelectionIndex();
0255 maxX = csm.getMaxSelectionIndex();
0256 minY = 0;
0257 maxY = table.getRowCount() - 1;
0258 // no selection allowed
0259 } else {
0260 totalCount = 0;
0261 // A bogus assignment to stop javac from complaining
0262 // about unitialized values. In this case, these
0263 // won't even be used.
0264 minX = maxX = minY = maxY = 0;
0265 }
0266
0267 // For some cases, there is no point in trying to stay within the
0268 // selected area. Instead, move outside the selection, wrapping at
0269 // the table boundaries. The cases are:
0270 boolean stayInSelection;
0271
0272 // - nothing selected
0273 if (totalCount == 0 ||
0274 // - one item selected, and the lead is already selected
0275 (totalCount == 1 && table.isCellSelected(leadRow,
0276 leadColumn))) {
0277
0278 stayInSelection = false;
0279
0280 maxX = table.getColumnCount() - 1;
0281 maxY = table.getRowCount() - 1;
0282
0283 // the mins are calculated like this in case the max is -1
0284 minX = Math.min(0, maxX);
0285 minY = Math.min(0, maxY);
0286 } else {
0287 stayInSelection = true;
0288 }
0289
0290 // the algorithm below isn't prepared to deal with -1 lead/anchor
0291 // so massage appropriately here first
0292 if (dy == 1 && leadColumn == -1) {
0293 leadColumn = minX;
0294 leadRow = -1;
0295 } else if (dx == 1 && leadRow == -1) {
0296 leadRow = minY;
0297 leadColumn = -1;
0298 } else if (dy == -1 && leadColumn == -1) {
0299 leadColumn = maxX;
0300 leadRow = maxY + 1;
0301 } else if (dx == -1 && leadRow == -1) {
0302 leadRow = maxY;
0303 leadColumn = maxX + 1;
0304 }
0305
0306 // In cases where the lead is not within the search range,
0307 // we need to bring it within one cell for the the search
0308 // to work properly. Check these here.
0309 leadRow = Math.min(Math.max(leadRow, minY - 1), maxY + 1);
0310 leadColumn = Math.min(Math.max(leadColumn, minX - 1),
0311 maxX + 1);
0312
0313 // find the next position, possibly looping until it is selected
0314 do {
0315 calcNextPos(dx, minX, maxX, dy, minY, maxY);
0316 } while (stayInSelection
0317 && !table.isCellSelected(leadRow, leadColumn));
0318
0319 return stayInSelection;
0320 }
0321
0322 /**
0323 * Find the next lead row and column based on the given
0324 * dx/dy and max/min values.
0325 */
0326 private void calcNextPos(int dx, int minX, int maxX, int dy,
0327 int minY, int maxY) {
0328
0329 if (dx != 0) {
0330 leadColumn += dx;
0331 if (leadColumn > maxX) {
0332 leadColumn = minX;
0333 leadRow++;
0334 if (leadRow > maxY) {
0335 leadRow = minY;
0336 }
0337 } else if (leadColumn < minX) {
0338 leadColumn = maxX;
0339 leadRow--;
0340 if (leadRow < minY) {
0341 leadRow = maxY;
0342 }
0343 }
0344 } else {
0345 leadRow += dy;
0346 if (leadRow > maxY) {
0347 leadRow = minY;
0348 leadColumn++;
0349 if (leadColumn > maxX) {
0350 leadColumn = minX;
0351 }
0352 } else if (leadRow < minY) {
0353 leadRow = maxY;
0354 leadColumn--;
0355 if (leadColumn < minX) {
0356 leadColumn = maxX;
0357 }
0358 }
0359 }
0360 }
0361
0362 public void actionPerformed(ActionEvent e) {
0363 String key = getName();
0364 JTable table = (JTable) e.getSource();
0365
0366 ListSelectionModel rsm = table.getSelectionModel();
0367 leadRow = getAdjustedLead(table, true, rsm);
0368
0369 ListSelectionModel csm = table.getColumnModel()
0370 .getSelectionModel();
0371 leadColumn = getAdjustedLead(table, false, csm);
0372
0373 if (key == SCROLL_LEFT_CHANGE_SELECTION
0374 || // Paging Actions
0375 key == SCROLL_LEFT_EXTEND_SELECTION
0376 || key == SCROLL_RIGHT_CHANGE_SELECTION
0377 || key == SCROLL_RIGHT_EXTEND_SELECTION
0378 || key == SCROLL_UP_CHANGE_SELECTION
0379 || key == SCROLL_UP_EXTEND_SELECTION
0380 || key == SCROLL_DOWN_CHANGE_SELECTION
0381 || key == SCROLL_DOWN_EXTEND_SELECTION
0382 || key == FIRST_COLUMN
0383 || key == FIRST_COLUMN_EXTEND_SELECTION
0384 || key == FIRST_ROW
0385 || key == FIRST_ROW_EXTEND_SELECTION
0386 || key == LAST_COLUMN
0387 || key == LAST_COLUMN_EXTEND_SELECTION
0388 || key == LAST_ROW
0389 || key == LAST_ROW_EXTEND_SELECTION) {
0390 if (toLimit) {
0391 if (vertically) {
0392 int rowCount = table.getRowCount();
0393 this .dx = 0;
0394 this .dy = forwards ? rowCount : -rowCount;
0395 } else {
0396 int colCount = table.getColumnCount();
0397 this .dx = forwards ? colCount : -colCount;
0398 this .dy = 0;
0399 }
0400 } else {
0401 if (!(table.getParent().getParent() instanceof JScrollPane)) {
0402 return;
0403 }
0404
0405 Dimension delta = table.getParent().getSize();
0406
0407 if (vertically) {
0408 Rectangle r = table.getCellRect(leadRow, 0,
0409 true);
0410 if (forwards) {
0411 // scroll by at least one cell
0412 r.y += Math.max(delta.height, r.height);
0413 } else {
0414 r.y -= delta.height;
0415 }
0416
0417 this .dx = 0;
0418 int newRow = table.rowAtPoint(r.getLocation());
0419 if (newRow == -1 && forwards) {
0420 newRow = table.getRowCount();
0421 }
0422 this .dy = newRow - leadRow;
0423 } else {
0424 Rectangle r = table.getCellRect(0, leadColumn,
0425 true);
0426
0427 if (forwards) {
0428 // scroll by at least one cell
0429 r.x += Math.max(delta.width, r.width);
0430 } else {
0431 r.x -= delta.width;
0432 }
0433
0434 int newColumn = table.columnAtPoint(r
0435 .getLocation());
0436 if (newColumn == -1) {
0437 boolean ltr = table
0438 .getComponentOrientation()
0439 .isLeftToRight();
0440
0441 newColumn = forwards ? (ltr ? table
0442 .getColumnCount() : 0) : (ltr ? 0
0443 : table.getColumnCount());
0444
0445 }
0446 this .dx = newColumn - leadColumn;
0447 this .dy = 0;
0448 }
0449 }
0450 }
0451 if (key == NEXT_ROW
0452 || // Navigate Actions
0453 key == NEXT_ROW_CELL
0454 || key == NEXT_ROW_EXTEND_SELECTION
0455 || key == NEXT_ROW_CHANGE_LEAD
0456 || key == NEXT_COLUMN
0457 || key == NEXT_COLUMN_CELL
0458 || key == NEXT_COLUMN_EXTEND_SELECTION
0459 || key == NEXT_COLUMN_CHANGE_LEAD
0460 || key == PREVIOUS_ROW
0461 || key == PREVIOUS_ROW_CELL
0462 || key == PREVIOUS_ROW_EXTEND_SELECTION
0463 || key == PREVIOUS_ROW_CHANGE_LEAD
0464 || key == PREVIOUS_COLUMN
0465 || key == PREVIOUS_COLUMN_CELL
0466 || key == PREVIOUS_COLUMN_EXTEND_SELECTION
0467 || key == PREVIOUS_COLUMN_CHANGE_LEAD
0468 ||
0469 // Paging Actions.
0470 key == SCROLL_LEFT_CHANGE_SELECTION
0471 || key == SCROLL_LEFT_EXTEND_SELECTION
0472 || key == SCROLL_RIGHT_CHANGE_SELECTION
0473 || key == SCROLL_RIGHT_EXTEND_SELECTION
0474 || key == SCROLL_UP_CHANGE_SELECTION
0475 || key == SCROLL_UP_EXTEND_SELECTION
0476 || key == SCROLL_DOWN_CHANGE_SELECTION
0477 || key == SCROLL_DOWN_EXTEND_SELECTION
0478 || key == FIRST_COLUMN
0479 || key == FIRST_COLUMN_EXTEND_SELECTION
0480 || key == FIRST_ROW
0481 || key == FIRST_ROW_EXTEND_SELECTION
0482 || key == LAST_COLUMN
0483 || key == LAST_COLUMN_EXTEND_SELECTION
0484 || key == LAST_ROW
0485 || key == LAST_ROW_EXTEND_SELECTION) {
0486
0487 if (table.isEditing()
0488 && !table.getCellEditor().stopCellEditing()) {
0489 return;
0490 }
0491
0492 // Unfortunately, this strategy introduces bugs because
0493 // of the asynchronous nature of requestFocus() call below.
0494 // Introducing a delay with invokeLater() makes this work
0495 // in the typical case though race conditions then allow
0496 // focus to disappear altogether. The right solution appears
0497 // to be to fix requestFocus() so that it queues a request
0498 // for the focus regardless of who owns the focus at the
0499 // time the call to requestFocus() is made. The optimisation
0500 // to ignore the call to requestFocus() when the component
0501 // already has focus may ligitimately be made as the
0502 // request focus event is dequeued, not before.
0503
0504 // boolean wasEditingWithFocus = table.isEditing() &&
0505 // table.getEditorComponent().isFocusOwner();
0506
0507 boolean changeLead = false;
0508 if (key == NEXT_ROW_CHANGE_LEAD
0509 || key == PREVIOUS_ROW_CHANGE_LEAD) {
0510 changeLead = (rsm.getSelectionMode() == ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
0511 } else if (key == NEXT_COLUMN_CHANGE_LEAD
0512 || key == PREVIOUS_COLUMN_CHANGE_LEAD) {
0513 changeLead = (csm.getSelectionMode() == ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
0514 }
0515
0516 if (changeLead) {
0517 moveWithinTableRange(table, dx, dy);
0518 if (dy != 0) {
0519 // casting should be safe since the action is only enabled
0520 // for DefaultListSelectionModel
0521 ((DefaultListSelectionModel) rsm)
0522 .moveLeadSelectionIndex(leadRow);
0523 if (getAdjustedLead(table, false, csm) == -1
0524 && table.getColumnCount() > 0) {
0525
0526 ((DefaultListSelectionModel) csm)
0527 .moveLeadSelectionIndex(0);
0528 }
0529 } else {
0530 // casting should be safe since the action is only enabled
0531 // for DefaultListSelectionModel
0532 ((DefaultListSelectionModel) csm)
0533 .moveLeadSelectionIndex(leadColumn);
0534 if (getAdjustedLead(table, true, rsm) == -1
0535 && table.getRowCount() > 0) {
0536
0537 ((DefaultListSelectionModel) rsm)
0538 .moveLeadSelectionIndex(0);
0539 }
0540 }
0541
0542 Rectangle cellRect = table.getCellRect(leadRow,
0543 leadColumn, false);
0544 if (cellRect != null) {
0545 table.scrollRectToVisible(cellRect);
0546 }
0547 } else if (!inSelection) {
0548 moveWithinTableRange(table, dx, dy);
0549 table.changeSelection(leadRow, leadColumn, false,
0550 extend);
0551 } else {
0552 if (table.getRowCount() <= 0
0553 || table.getColumnCount() <= 0) {
0554 // bail - don't try to move selection on an empty table
0555 return;
0556 }
0557
0558 if (moveWithinSelectedRange(table, dx, dy, rsm, csm)) {
0559 // this is the only way we have to set both the lead
0560 // and the anchor without changing the selection
0561 if (rsm.isSelectedIndex(leadRow)) {
0562 rsm.addSelectionInterval(leadRow, leadRow);
0563 } else {
0564 rsm.removeSelectionInterval(leadRow,
0565 leadRow);
0566 }
0567
0568 if (csm.isSelectedIndex(leadColumn)) {
0569 csm.addSelectionInterval(leadColumn,
0570 leadColumn);
0571 } else {
0572 csm.removeSelectionInterval(leadColumn,
0573 leadColumn);
0574 }
0575
0576 Rectangle cellRect = table.getCellRect(leadRow,
0577 leadColumn, false);
0578 if (cellRect != null) {
0579 table.scrollRectToVisible(cellRect);
0580 }
0581 } else {
0582 table.changeSelection(leadRow, leadColumn,
0583 false, false);
0584 }
0585 }
0586
0587 /*
0588 if (wasEditingWithFocus) {
0589 table.editCellAt(leadRow, leadColumn);
0590 final Component editorComp = table.getEditorComponent();
0591 if (editorComp != null) {
0592 SwingUtilities.invokeLater(new Runnable() {
0593 public void run() {
0594 editorComp.requestFocus();
0595 }
0596 });
0597 }
0598 }
0599 */
0600 } else if (key == CANCEL_EDITING) {
0601 table.removeEditor();
0602 } else if (key == SELECT_ALL) {
0603 table.selectAll();
0604 } else if (key == CLEAR_SELECTION) {
0605 table.clearSelection();
0606 } else if (key == START_EDITING) {
0607 if (!table.hasFocus()) {
0608 CellEditor cellEditor = table.getCellEditor();
0609 if (cellEditor != null
0610 && !cellEditor.stopCellEditing()) {
0611 return;
0612 }
0613 table.requestFocus();
0614 return;
0615 }
0616 table.editCellAt(leadRow, leadColumn, e);
0617 Component editorComp = table.getEditorComponent();
0618 if (editorComp != null) {
0619 editorComp.requestFocus();
0620 }
0621 } else if (key == ADD_TO_SELECTION) {
0622 if (!table.isCellSelected(leadRow, leadColumn)) {
0623 int oldAnchorRow = rsm.getAnchorSelectionIndex();
0624 int oldAnchorColumn = csm.getAnchorSelectionIndex();
0625 rsm.setValueIsAdjusting(true);
0626 csm.setValueIsAdjusting(true);
0627 table.changeSelection(leadRow, leadColumn, true,
0628 false);
0629 rsm.setAnchorSelectionIndex(oldAnchorRow);
0630 csm.setAnchorSelectionIndex(oldAnchorColumn);
0631 rsm.setValueIsAdjusting(false);
0632 csm.setValueIsAdjusting(false);
0633 }
0634 } else if (key == TOGGLE_AND_ANCHOR) {
0635 table.changeSelection(leadRow, leadColumn, true, false);
0636 } else if (key == EXTEND_TO) {
0637 table.changeSelection(leadRow, leadColumn, false, true);
0638 } else if (key == MOVE_SELECTION_TO) {
0639 table
0640 .changeSelection(leadRow, leadColumn, false,
0641 false);
0642 } else if (key == FOCUS_HEADER) {
0643 JTableHeader th = table.getTableHeader();
0644 if (th != null) {
0645 //Set the header's selected column to match the table.
0646 int col = table.getSelectedColumn();
0647 if (col >= 0) {
0648 TableHeaderUI thUI = th.getUI();
0649 if (thUI instanceof BasicTableHeaderUI) {
0650 ((BasicTableHeaderUI) thUI)
0651 .selectColumn(col);
0652 }
0653 }
0654
0655 //Then give the header the focus.
0656 th.requestFocusInWindow();
0657 }
0658 }
0659 }
0660
0661 public boolean isEnabled(Object sender) {
0662 String key = getName();
0663
0664 if (sender instanceof JTable
0665 && Boolean.TRUE.equals(((JTable) sender)
0666 .getClientProperty("Table.isFileList"))) {
0667 if (key == NEXT_COLUMN || key == NEXT_COLUMN_CELL
0668 || key == NEXT_COLUMN_EXTEND_SELECTION
0669 || key == NEXT_COLUMN_CHANGE_LEAD
0670 || key == PREVIOUS_COLUMN
0671 || key == PREVIOUS_COLUMN_CELL
0672 || key == PREVIOUS_COLUMN_EXTEND_SELECTION
0673 || key == PREVIOUS_COLUMN_CHANGE_LEAD
0674 || key == SCROLL_LEFT_CHANGE_SELECTION
0675 || key == SCROLL_LEFT_EXTEND_SELECTION
0676 || key == SCROLL_RIGHT_CHANGE_SELECTION
0677 || key == SCROLL_RIGHT_EXTEND_SELECTION
0678 || key == FIRST_COLUMN
0679 || key == FIRST_COLUMN_EXTEND_SELECTION
0680 || key == LAST_COLUMN
0681 || key == LAST_COLUMN_EXTEND_SELECTION
0682 || key == NEXT_ROW_CELL
0683 || key == PREVIOUS_ROW_CELL) {
0684
0685 return false;
0686 }
0687 }
0688
0689 if (key == CANCEL_EDITING && sender instanceof JTable) {
0690 return ((JTable) sender).isEditing();
0691 } else if (key == NEXT_ROW_CHANGE_LEAD
0692 || key == PREVIOUS_ROW_CHANGE_LEAD) {
0693 // discontinuous selection actions are only enabled for
0694 // DefaultListSelectionModel
0695 return sender != null
0696 && ((JTable) sender).getSelectionModel() instanceof DefaultListSelectionModel;
0697 } else if (key == NEXT_COLUMN_CHANGE_LEAD
0698 || key == PREVIOUS_COLUMN_CHANGE_LEAD) {
0699 // discontinuous selection actions are only enabled for
0700 // DefaultListSelectionModel
0701 return sender != null
0702 && ((JTable) sender).getColumnModel()
0703 .getSelectionModel() instanceof DefaultListSelectionModel;
0704 } else if (key == ADD_TO_SELECTION
0705 && sender instanceof JTable) {
0706 // This action is typically bound to SPACE.
0707 // If the table is already in an editing mode, SPACE should
0708 // simply enter a space character into the table, and not
0709 // select a cell. Likewise, if the lead cell is already selected
0710 // then hitting SPACE should just enter a space character
0711 // into the cell and begin editing. In both of these cases
0712 // this action will be disabled.
0713 JTable table = (JTable) sender;
0714 int leadRow = getAdjustedLead(table, true);
0715 int leadCol = getAdjustedLead(table, false);
0716 return !(table.isEditing() || table.isCellSelected(
0717 leadRow, leadCol));
0718 } else if (key == FOCUS_HEADER && sender instanceof JTable) {
0719 JTable table = (JTable) sender;
0720 return table.getTableHeader() != null;
0721 }
0722
0723 return true;
0724 }
0725 }
0726
0727 //
0728 // The Table's Key listener
0729 //
0730
0731 /**
0732 * This inner class is marked "public" due to a compiler bug.
0733 * This class should be treated as a "protected" inner class.
0734 * Instantiate it only within subclasses of BasicTableUI.
0735 * <p>As of Java 2 platform v1.3 this class is no longer used.
0736 * Instead <code>JTable</code>
0737 * overrides <code>processKeyBinding</code> to dispatch the event to
0738 * the current <code>TableCellEditor</code>.
0739 */
0740 public class KeyHandler implements KeyListener {
0741 // NOTE: This class exists only for backward compatability. All
0742 // its functionality has been moved into Handler. If you need to add
0743 // new functionality add it to the Handler, but make sure this
0744 // class calls into the Handler.
0745 public void keyPressed(KeyEvent e) {
0746 getHandler().keyPressed(e);
0747 }
0748
0749 public void keyReleased(KeyEvent e) {
0750 getHandler().keyReleased(e);
0751 }
0752
0753 public void keyTyped(KeyEvent e) {
0754 getHandler().keyTyped(e);
0755 }
0756 }
0757
0758 //
0759 // The Table's focus listener
0760 //
0761
0762 /**
0763 * This inner class is marked "public" due to a compiler bug.
0764 * This class should be treated as a "protected" inner class.
0765 * Instantiate it only within subclasses of BasicTableUI.
0766 */
0767 public class FocusHandler implements FocusListener {
0768 // NOTE: This class exists only for backward compatability. All
0769 // its functionality has been moved into Handler. If you need to add
0770 // new functionality add it to the Handler, but make sure this
0771 // class calls into the Handler.
0772 public void focusGained(FocusEvent e) {
0773 getHandler().focusGained(e);
0774 }
0775
0776 public void focusLost(FocusEvent e) {
0777 getHandler().focusLost(e);
0778 }
0779 }
0780
0781 //
0782 // The Table's mouse and mouse motion listeners
0783 //
0784
0785 /**
0786 * This inner class is marked "public" due to a compiler bug.
0787 * This class should be treated as a "protected" inner class.
0788 * Instantiate it only within subclasses of BasicTableUI.
0789 */
0790 public class MouseInputHandler implements MouseInputListener {
0791 // NOTE: This class exists only for backward compatability. All
0792 // its functionality has been moved into Handler. If you need to add
0793 // new functionality add it to the Handler, but make sure this
0794 // class calls into the Handler.
0795 public void mouseClicked(MouseEvent e) {
0796 getHandler().mouseClicked(e);
0797 }
0798
0799 public void mousePressed(MouseEvent e) {
0800 getHandler().mousePressed(e);
0801 }
0802
0803 public void mouseReleased(MouseEvent e) {
0804 getHandler().mouseReleased(e);
0805 }
0806
0807 public void mouseEntered(MouseEvent e) {
0808 getHandler().mouseEntered(e);
0809 }
0810
0811 public void mouseExited(MouseEvent e) {
0812 getHandler().mouseExited(e);
0813 }
0814
0815 public void mouseMoved(MouseEvent e) {
0816 getHandler().mouseMoved(e);
0817 }
0818
0819 public void mouseDragged(MouseEvent e) {
0820 getHandler().mouseDragged(e);
0821 }
0822 }
0823
0824 private class Handler implements FocusListener, MouseInputListener,
0825 PropertyChangeListener, ListSelectionListener,
0826 ActionListener, BeforeDrag {
0827
0828 // FocusListener
0829 private void repaintLeadCell() {
0830 int lr = getAdjustedLead(table, true);
0831 int lc = getAdjustedLead(table, false);
0832
0833 if (lr < 0 || lc < 0) {
0834 return;
0835 }
0836
0837 Rectangle dirtyRect = table.getCellRect(lr, lc, false);
0838 table.repaint(dirtyRect);
0839 }
0840
0841 public void focusGained(FocusEvent e) {
0842 repaintLeadCell();
0843 }
0844
0845 public void focusLost(FocusEvent e) {
0846 repaintLeadCell();
0847 }
0848
0849 // KeyListener
0850 public void keyPressed(KeyEvent e) {
0851 }
0852
0853 public void keyReleased(KeyEvent e) {
0854 }
0855
0856 public void keyTyped(KeyEvent e) {
0857 KeyStroke keyStroke = KeyStroke.getKeyStroke(
0858 e.getKeyChar(), e.getModifiers());
0859
0860 // We register all actions using ANCESTOR_OF_FOCUSED_COMPONENT
0861 // which means that we might perform the appropriate action
0862 // in the table and then forward it to the editor if the editor
0863 // had focus. Make sure this doesn't happen by checking our
0864 // InputMaps.
0865 InputMap map = table.getInputMap(JComponent.WHEN_FOCUSED);
0866 if (map != null && map.get(keyStroke) != null) {
0867 return;
0868 }
0869 map = table
0870 .getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0871 if (map != null && map.get(keyStroke) != null) {
0872 return;
0873 }
0874
0875 keyStroke = KeyStroke.getKeyStrokeForEvent(e);
0876
0877 // The AWT seems to generate an unconsumed \r event when
0878 // ENTER (\n) is pressed.
0879 if (e.getKeyChar() == '\r') {
0880 return;
0881 }
0882
0883 int leadRow = getAdjustedLead(table, true);
0884 int leadColumn = getAdjustedLead(table, false);
0885 if (leadRow != -1 && leadColumn != -1 && !table.isEditing()) {
0886 if (!table.editCellAt(leadRow, leadColumn)) {
0887 return;
0888 }
0889 }
0890
0891 // Forwarding events this way seems to put the component
0892 // in a state where it believes it has focus. In reality
0893 // the table retains focus - though it is difficult for
0894 // a user to tell, since the caret is visible and flashing.
0895
0896 // Calling table.requestFocus() here, to get the focus back to
0897 // the table, seems to have no effect.
0898
0899 Component editorComp = table.getEditorComponent();
0900 if (table.isEditing() && editorComp != null) {
0901 if (editorComp instanceof JComponent) {
0902 JComponent component = (JComponent) editorComp;
0903 map = component
0904 .getInputMap(JComponent.WHEN_FOCUSED);
0905 Object binding = (map != null) ? map.get(keyStroke)
0906 : null;
0907 if (binding == null) {
0908 map = component
0909 .getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
0910 binding = (map != null) ? map.get(keyStroke)
0911 : null;
0912 }
0913 if (binding != null) {
0914 ActionMap am = component.getActionMap();
0915 Action action = (am != null) ? am.get(binding)
0916 : null;
0917 if (action != null
0918 && SwingUtilities.notifyAction(action,
0919 keyStroke, e, component, e
0920 .getModifiers())) {
0921 e.consume();
0922 }
0923 }
0924 }
0925 }
0926 }
0927
0928 // MouseInputListener
0929
0930 // Component receiving mouse events during editing.
0931 // May not be editorComponent.
0932 private Component dispatchComponent;
0933
0934 public void mouseClicked(MouseEvent e) {
0935 }
0936
0937 private void setDispatchComponent(MouseEvent e) {
0938 Component editorComponent = table.getEditorComponent();
0939 Point p = e.getPoint();
0940 Point p2 = SwingUtilities.convertPoint(table, p,
0941 editorComponent);
0942 dispatchComponent = SwingUtilities.getDeepestComponentAt(
0943 editorComponent, p2.x, p2.y);
0944 SwingUtilities2.setSkipClickCount(dispatchComponent, e
0945 .getClickCount() - 1);
0946 }
0947
0948 private boolean repostEvent(MouseEvent e) {
0949 // Check for isEditing() in case another event has
0950 // caused the editor to be removed. See bug #4306499.
0951 if (dispatchComponent == null || !table.isEditing()) {
0952 return false;
0953 }
0954 MouseEvent e2 = SwingUtilities.convertMouseEvent(table, e,
0955 dispatchComponent);
0956 dispatchComponent.dispatchEvent(e2);
0957 return true;
0958 }
0959
0960 private void setValueIsAdjusting(boolean flag) {
0961 table.getSelectionModel().setValueIsAdjusting(flag);
0962 table.getColumnModel().getSelectionModel()
0963 .setValueIsAdjusting(flag);
0964 }
0965
0966 // The row and column where the press occurred and the
0967 // press event itself
0968 private int pressedRow;
0969 private int pressedCol;
0970 private MouseEvent pressedEvent;
0971
0972 // Whether or not the mouse press (which is being considered as part
0973 // of a drag sequence) also caused the selection change to be fully
0974 // processed.
0975 private boolean dragPressDidSelection;
0976
0977 // Set to true when a drag gesture has been fully recognized and DnD
0978 // begins. Use this to ignore further mouse events which could be
0979 // delivered if DnD is cancelled (via ESCAPE for example)
0980 private boolean dragStarted;
0981
0982 // Whether or not we should start the editing timer on release
0983 private boolean shouldStartTimer;
0984
0985 // To cache the return value of pointOutsidePrefSize since we use
0986 // it multiple times.
0987 private boolean outsidePrefSize;
0988
0989 // Used to delay the start of editing.
0990 private Timer timer = null;
0991
0992 private boolean canStartDrag() {
0993 if (pressedRow == -1 || pressedCol == -1) {
0994 return false;
0995 }
0996
0997 if (isFileList) {
0998 return !outsidePrefSize;
0999 }
1000
1001 // if this is a single selection table
1002 if ((table.getSelectionModel().getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)
1003 && (table.getColumnModel().getSelectionModel()
1004 .getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)) {
1005
1006 return true;
1007 }
1008
1009 return table.isCellSelected(pressedRow, pressedCol);
1010 }
1011
1012 public void mousePressed(MouseEvent e) {
1013 if (SwingUtilities2.shouldIgnore(e, table)) {
1014 return;
1015 }
1016
1017 if (table.isEditing()
1018 && !table.getCellEditor().stopCellEditing()) {
1019 Component editorComponent = table.getEditorComponent();
1020 if (editorComponent != null
1021 && !editorComponent.hasFocus()) {
1022 SwingUtilities2
1023 .compositeRequestFocus(editorComponent);
1024 }
1025 return;
1026 }
1027
1028 Point p = e.getPoint();
1029 pressedRow = table.rowAtPoint(p);
1030 pressedCol = table.columnAtPoint(p);
1031 outsidePrefSize = pointOutsidePrefSize(pressedRow,
1032 pressedCol, p);
1033
1034 if (isFileList) {
1035 shouldStartTimer = table.isCellSelected(pressedRow,
1036 pressedCol)
1037 && !e.isShiftDown()
1038 && !e.isControlDown()
1039 && !outsidePrefSize;
1040 }
1041
1042 if (table.getDragEnabled()) {
1043 mousePressedDND(e);
1044 } else {
1045 SwingUtilities2.adjustFocus(table);
1046 if (!isFileList) {
1047 setValueIsAdjusting(true);
1048 }
1049 adjustSelection(e);
1050 }
1051 }
1052
1053 private void mousePressedDND(MouseEvent e) {
1054 pressedEvent = e;
1055 boolean grabFocus = true;
1056 dragStarted = false;
1057
1058 if (canStartDrag()
1059 && DragRecognitionSupport.mousePressed(e)) {
1060
1061 dragPressDidSelection = false;
1062
1063 if (e.isControlDown() && isFileList) {
1064 // do nothing for control - will be handled on release
1065 // or when drag starts
1066 return;
1067 } else if (!e.isShiftDown()
1068 && table.isCellSelected(pressedRow, pressedCol)) {
1069 // clicking on something that's already selected
1070 // and need to make it the lead now
1071 table.getSelectionModel().addSelectionInterval(
1072 pressedRow, pressedRow);
1073 table.getColumnModel().getSelectionModel()
1074 .addSelectionInterval(pressedCol,
1075 pressedCol);
1076
1077 return;
1078 }
1079
1080 dragPressDidSelection = true;
1081
1082 // could be a drag initiating event - don't grab focus
1083 grabFocus = false;
1084 } else if (!isFileList) {
1085 // When drag can't happen, mouse drags might change the selection in the table
1086 // so we want the isAdjusting flag to be set
1087 setValueIsAdjusting(true);
1088 }
1089
1090 if (grabFocus) {
1091 SwingUtilities2.adjustFocus(table);
1092 }
1093
1094 adjustSelection(e);
1095 }
1096
1097 private void adjustSelection(MouseEvent e) {
1098 // Fix for 4835633
1099 if (outsidePrefSize) {
1100 // If shift is down in multi-select, we should just return.
1101 // For single select or non-shift-click, clear the selection
1102 if (e.getID() == MouseEvent.MOUSE_PRESSED
1103 && (!e.isShiftDown() || table
1104 .getSelectionModel().getSelectionMode() == ListSelectionModel.SINGLE_SELECTION)) {
1105 table.clearSelection();
1106 TableCellEditor tce = table.getCellEditor();
1107 if (tce != null) {
1108 tce.stopCellEditing();
1109 }
1110 }
1111 return;
1112 }
1113 // The autoscroller can generate drag events outside the
1114 // table's range.
1115 if ((pressedCol == -1) || (pressedRow == -1)) {
1116 return;
1117 }
1118
1119 boolean dragEnabled = table.getDragEnabled();
1120
1121 if (!dragEnabled && !isFileList
1122 && table.editCellAt(pressedRow, pressedCol, e)) {
1123 setDispatchComponent(e);
1124 repostEvent(e);
1125 }
1126
1127 CellEditor editor = table.getCellEditor();
1128 if (dragEnabled || editor == null
1129 || editor.shouldSelectCell(e)) {
1130 table.changeSelection(pressedRow, pressedCol, e
1131 .isControlDown(), e.isShiftDown());
1132 }
1133 }
1134
1135 public void valueChanged(ListSelectionEvent e) {
1136 if (timer != null) {
1137 timer.stop();
1138 timer = null;
1139 }
1140 }
1141
1142 public void actionPerformed(ActionEvent ae) {
1143 table.editCellAt(pressedRow, pressedCol, null);
1144 Component editorComponent = table.getEditorComponent();
1145 if (editorComponent != null && !editorComponent.hasFocus()) {
1146 SwingUtilities2.compositeRequestFocus(editorComponent);
1147 }
1148 return;
1149 }
1150
1151 private void maybeStartTimer() {
1152 if (!shouldStartTimer) {
1153 return;
1154 }
1155
1156 if (timer == null) {
1157 timer = new Timer(1200, this );
1158 timer.setRepeats(false);
1159 }
1160
1161 timer.start();
1162 }
1163
1164 public void mouseReleased(MouseEvent e) {
1165 if (SwingUtilities2.shouldIgnore(e, table)) {
1166 return;
1167 }
1168
1169 if (table.getDragEnabled()) {
1170 mouseReleasedDND(e);
1171 } else {
1172 if (isFileList) {
1173 maybeStartTimer();
1174 }
1175 }
1176
1177 pressedEvent = null;
1178 repostEvent(e);
1179 dispatchComponent = null;
1180 setValueIsAdjusting(false);
1181 }
1182
1183 private void mouseReleasedDND(MouseEvent e) {
1184 MouseEvent me = DragRecognitionSupport.mouseReleased(e);
1185 if (me != null) {
1186 SwingUtilities2.adjustFocus(table);
1187 if (!dragPressDidSelection) {
1188 adjustSelection(me);
1189 }
1190 }
1191
1192 if (!dragStarted) {
1193 if (isFileList) {
1194 maybeStartTimer();
1195 return;
1196 }
1197
1198 Point p = e.getPoint();
1199
1200 if (pressedEvent != null
1201 && table.rowAtPoint(p) == pressedRow
1202 && table.columnAtPoint(p) == pressedCol
1203 && table.editCellAt(pressedRow, pressedCol,
1204 pressedEvent)) {
1205
1206 setDispatchComponent(pressedEvent);
1207 repostEvent(pressedEvent);
1208
1209 // This may appear completely odd, but must be done for backward
1210 // compatibility reasons. Developers have been known to rely on
1211 // a call to shouldSelectCell after editing has begun.
1212 CellEditor ce = table.getCellEditor();
1213 if (ce != null) {
1214 ce.shouldSelectCell(pressedEvent);
1215 }
1216 }
1217 }
1218 }
1219
1220 public void mouseEntered(MouseEvent e) {
1221 }
1222
1223 public void mouseExited(MouseEvent e) {
1224 }
1225
1226 public void mouseMoved(MouseEvent e) {
1227 }
1228
1229 public void dragStarting(MouseEvent me) {
1230 dragStarted = true;
1231
1232 if (me.isControlDown() && isFileList) {
1233 table.getSelectionModel().addSelectionInterval(
1234 pressedRow, pressedRow);
1235 table.getColumnModel().getSelectionModel()
1236 .addSelectionInterval(pressedCol, pressedCol);
1237 }
1238
1239 pressedEvent = null;
1240 }
1241
1242 public void mouseDragged(MouseEvent e) {
1243 if (SwingUtilities2.shouldIgnore(e, table)) {
1244 return;
1245 }
1246
1247 if (table.getDragEnabled()
1248 && (DragRecognitionSupport.mouseDragged(e, this ) || dragStarted)) {
1249
1250 return;
1251 }
1252
1253 repostEvent(e);
1254
1255 // Check isFileList:
1256 // Until we support drag-selection, dragging should not change
1257 // the selection (act like single-select).
1258 if (isFileList || table.isEditing()) {
1259 return;
1260 }
1261
1262 Point p = e.getPoint();
1263 int row = table.rowAtPoint(p);
1264 int column = table.columnAtPoint(p);
1265 // The autoscroller can generate drag events outside the
1266 // table's range.
1267 if ((column == -1) || (row == -1)) {
1268 return;
1269 }
1270
1271 table.changeSelection(row, column, e.isControlDown(), true);
1272 }
1273
1274 // PropertyChangeListener
1275 public void propertyChange(PropertyChangeEvent event) {
1276 String changeName = event.getPropertyName();
1277
1278 if ("componentOrientation" == changeName) {
1279 InputMap inputMap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
1280
1281 SwingUtilities.replaceUIInputMap(table,
1282 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
1283 inputMap);
1284
1285 JTableHeader header = table.getTableHeader();
1286 if (header != null) {
1287 header
1288 .setComponentOrientation((ComponentOrientation) event
1289 .getNewValue());
1290 }
1291 } else if ("dropLocation" == changeName) {
1292 JTable.DropLocation oldValue = (JTable.DropLocation) event
1293 .getOldValue();
1294 repaintDropLocation(oldValue);
1295 repaintDropLocation(table.getDropLocation());
1296 } else if ("Table.isFileList" == changeName) {
1297 isFileList = Boolean.TRUE.equals(table
1298 .getClientProperty("Table.isFileList"));
1299 table.revalidate();
1300 table.repaint();
1301 if (isFileList) {
1302 table.getSelectionModel().addListSelectionListener(
1303 getHandler());
1304 } else {
1305 table.getSelectionModel()
1306 .removeListSelectionListener(getHandler());
1307 timer = null;
1308 }
1309 } else if ("selectionModel" == changeName) {
1310 if (isFileList) {
1311 ListSelectionModel old = (ListSelectionModel) event
1312 .getOldValue();
1313 old.removeListSelectionListener(getHandler());
1314 table.getSelectionModel().addListSelectionListener(
1315 getHandler());
1316 }
1317 }
1318 }
1319
1320 private void repaintDropLocation(JTable.DropLocation loc) {
1321 if (loc == null) {
1322 return;
1323 }
1324
1325 if (!loc.isInsertRow() && !loc.isInsertColumn()) {
1326 Rectangle rect = table.getCellRect(loc.getRow(), loc
1327 .getColumn(), false);
1328 if (rect != null) {
1329 table.repaint(rect);
1330 }
1331 return;
1332 }
1333
1334 if (loc.isInsertRow()) {
1335 Rectangle rect = extendRect(getHDropLineRect(loc), true);
1336 if (rect != null) {
1337 table.repaint(rect);
1338 }
1339 }
1340
1341 if (loc.isInsertColumn()) {
1342 Rectangle rect = extendRect(getVDropLineRect(loc),
1343 false);
1344 if (rect != null) {
1345 table.repaint(rect);
1346 }
1347 }
1348 }
1349 }
1350
1351 /*
1352 * Returns true if the given point is outside the preferredSize of the
1353 * item at the given row of the table. (Column must be 0).
1354 * Returns false if the "Table.isFileList" client property is not set.
1355 */
1356 private boolean pointOutsidePrefSize(int row, int column, Point p) {
1357 if (!isFileList) {
1358 return false;
1359 }
1360
1361 return SwingUtilities2.pointOutsidePrefSize(table, row, column,
1362 p);
1363 }
1364
1365 //
1366 // Factory methods for the Listeners
1367 //
1368
1369 private Handler getHandler() {
1370 if (handler == null) {
1371 handler = new Handler();
1372 }
1373 return handler;
1374 }
1375
1376 /**
1377 * Creates the key listener for handling keyboard navigation in the JTable.
1378 */
1379 protected KeyListener createKeyListener() {
1380 return null;
1381 }
1382
1383 /**
1384 * Creates the focus listener for handling keyboard navigation in the JTable.
1385 */
1386 protected FocusListener createFocusListener() {
1387 return getHandler();
1388 }
1389
1390 /**
1391 * Creates the mouse listener for the JTable.
1392 */
1393 protected MouseInputListener createMouseInputListener() {
1394 return getHandler();
1395 }
1396
1397 //
1398 // The installation/uninstall procedures and support
1399 //
1400
1401 public static ComponentUI createUI(JComponent c) {
1402 return new BasicTableUI();
1403 }
1404
1405 // Installation
1406
1407 public void installUI(JComponent c) {
1408 table = (JTable) c;
1409
1410 rendererPane = new CellRendererPane();
1411 table.add(rendererPane);
1412 installDefaults();
1413 installDefaults2();
1414 installListeners();
1415 installKeyboardActions();
1416 }
1417
1418 /**
1419 * Initialize JTable properties, e.g. font, foreground, and background.
1420 * The font, foreground, and background properties are only set if their
1421 * current value is either null or a UIResource, other properties are set
1422 * if the current value is null.
1423 *
1424 * @see #installUI
1425 */
1426 protected void installDefaults() {
1427 LookAndFeel.installColorsAndFont(table, "Table.background",
1428 "Table.foreground", "Table.font");
1429 // JTable's original row height is 16. To correctly display the
1430 // contents on Linux we should have set it to 18, Windows 19 and
1431 // Solaris 20. As these values vary so much it's too hard to
1432 // be backward compatable and try to update the row height, we're
1433 // therefor NOT going to adjust the row height based on font. If the
1434 // developer changes the font, it's there responsability to update
1435 // the row height.
1436
1437 LookAndFeel.installProperty(table, "opaque", Boolean.TRUE);
1438
1439 Color sbg = table.getSelectionBackground();
1440 if (sbg == null || sbg instanceof UIResource) {
1441 table.setSelectionBackground(UIManager
1442 .getColor("Table.selectionBackground"));
1443 }
1444
1445 Color sfg = table.getSelectionForeground();
1446 if (sfg == null || sfg instanceof UIResource) {
1447 table.setSelectionForeground(UIManager
1448 .getColor("Table.selectionForeground"));
1449 }
1450
1451 Color gridColor = table.getGridColor();
1452 if (gridColor == null || gridColor instanceof UIResource) {
1453 table.setGridColor(UIManager.getColor("Table.gridColor"));
1454 }
1455
1456 // install the scrollpane border
1457 Container parent = table.getParent(); // should be viewport
1458 if (parent != null) {
1459 parent = parent.getParent(); // should be the scrollpane
1460 if (parent != null && parent instanceof JScrollPane) {
1461 LookAndFeel.installBorder((JScrollPane) parent,
1462 "Table.scrollPaneBorder");
1463 }
1464 }
1465
1466 isFileList = Boolean.TRUE.equals(table
1467 .getClientProperty("Table.isFileList"));
1468 }
1469
1470 private void installDefaults2() {
1471 TransferHandler th = table.getTransferHandler();
1472 if (th == null || th instanceof UIResource) {
1473 table.setTransferHandler(defaultTransferHandler);
1474 // default TransferHandler doesn't support drop
1475 // so we don't want drop handling
1476 if (table.getDropTarget() instanceof UIResource) {
1477 table.setDropTarget(null);
1478 }
1479 }
1480 }
1481
1482 /**
1483 * Attaches listeners to the JTable.
1484 */
1485 protected void installListeners() {
1486 focusListener = createFocusListener();
1487 keyListener = createKeyListener();
1488 mouseInputListener = createMouseInputListener();
1489
1490 table.addFocusListener(focusListener);
1491 table.addKeyListener(keyListener);
1492 table.addMouseListener(mouseInputListener);
1493 table.addMouseMotionListener(mouseInputListener);
1494 table.addPropertyChangeListener(getHandler());
1495 if (isFileList) {
1496 table.getSelectionModel().addListSelectionListener(
1497 getHandler());
1498 }
1499 }
1500
1501 /**
1502 * Register all keyboard actions on the JTable.
1503 */
1504 protected void installKeyboardActions() {
1505 LazyActionMap.installLazyActionMap(table, BasicTableUI.class,
1506 "Table.actionMap");
1507
1508 InputMap inputMap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
1509 SwingUtilities
1510 .replaceUIInputMap(table,
1511 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
1512 inputMap);
1513 }
1514
1515 InputMap getInputMap(int condition) {
1516 if (condition == JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT) {
1517 InputMap keyMap = (InputMap) DefaultLookup.get(table, this ,
1518 "Table.ancestorInputMap");
1519 InputMap rtlKeyMap;
1520
1521 if (table.getComponentOrientation().isLeftToRight()
1522 || ((rtlKeyMap = (InputMap) DefaultLookup.get(
1523 table, this ,
1524 "Table.ancestorInputMap.RightToLeft")) == null)) {
1525 return keyMap;
1526 } else {
1527 rtlKeyMap.setParent(keyMap);
1528 return rtlKeyMap;
1529 }
1530 }
1531 return null;
1532 }
1533
1534 static void loadActionMap(LazyActionMap map) {
1535 // IMPORTANT: There is a very close coupling between the parameters
1536 // passed to the Actions constructor. Only certain parameter
1537 // combinations are supported. For example, the following Action would
1538 // not work as expected:
1539 // new Actions(Actions.NEXT_ROW_CELL, 1, 4, false, true)
1540 // Actions which move within the selection only (having a true
1541 // inSelection parameter) require that one of dx or dy be
1542 // zero and the other be -1 or 1. The point of this warning is
1543 // that you should be very careful about making sure a particular
1544 // combination of parameters is supported before changing or
1545 // adding anything here.
1546
1547 map.put(new Actions(Actions.NEXT_COLUMN, 1, 0, false, false));
1548 map.put(new Actions(Actions.NEXT_COLUMN_CHANGE_LEAD, 1, 0,
1549 false, false));
1550 map.put(new Actions(Actions.PREVIOUS_COLUMN, -1, 0, false,
1551 false));
1552 map.put(new Actions(Actions.PREVIOUS_COLUMN_CHANGE_LEAD, -1, 0,
1553 false, false));
1554 map.put(new Actions(Actions.NEXT_ROW, 0, 1, false, false));
1555 map.put(new Actions(Actions.NEXT_ROW_CHANGE_LEAD, 0, 1, false,
1556 false));
1557 map.put(new Actions(Actions.PREVIOUS_ROW, 0, -1, false, false));
1558 map.put(new Actions(Actions.PREVIOUS_ROW_CHANGE_LEAD, 0, -1,
1559 false, false));
1560 map.put(new Actions(Actions.NEXT_COLUMN_EXTEND_SELECTION, 1, 0,
1561 true, false));
1562 map.put(new Actions(Actions.PREVIOUS_COLUMN_EXTEND_SELECTION,
1563 -1, 0, true, false));
1564 map.put(new Actions(Actions.NEXT_ROW_EXTEND_SELECTION, 0, 1,
1565 true, false));
1566 map.put(new Actions(Actions.PREVIOUS_ROW_EXTEND_SELECTION, 0,
1567 -1, true, false));
1568 map.put(new Actions(Actions.SCROLL_UP_CHANGE_SELECTION, false,
1569 false, true, false));
1570 map.put(new Actions(Actions.SCROLL_DOWN_CHANGE_SELECTION,
1571 false, true, true, false));
1572 map.put(new Actions(Actions.FIRST_COLUMN, false, false, false,
1573 true));
1574 map.put(new Actions(Actions.LAST_COLUMN, false, true, false,
1575 true));
1576
1577 map.put(new Actions(Actions.SCROLL_UP_EXTEND_SELECTION, true,
1578 false, true, false));
1579 map.put(new Actions(Actions.SCROLL_DOWN_EXTEND_SELECTION, true,
1580 true, true, false));
1581 map.put(new Actions(Actions.FIRST_COLUMN_EXTEND_SELECTION,
1582 true, false, false, true));
1583 map.put(new Actions(Actions.LAST_COLUMN_EXTEND_SELECTION, true,
1584 true, false, true));
1585
1586 map
1587 .put(new Actions(Actions.FIRST_ROW, false, false, true,
1588 true));
1589 map.put(new Actions(Actions.LAST_ROW, false, true, true, true));
1590
1591 map.put(new Actions(Actions.FIRST_ROW_EXTEND_SELECTION, true,
1592 false, true, true));
1593 map.put(new Actions(Actions.LAST_ROW_EXTEND_SELECTION, true,
1594 true, true, true));
1595
1596 map
1597 .put(new Actions(Actions.NEXT_COLUMN_CELL, 1, 0, false,
1598 true));
1599 map.put(new Actions(Actions.PREVIOUS_COLUMN_CELL, -1, 0, false,
1600 true));
1601 map.put(new Actions(Actions.NEXT_ROW_CELL, 0, 1, false, true));
1602 map.put(new Actions(Actions.PREVIOUS_ROW_CELL, 0, -1, false,
1603 true));
1604
1605 map.put(new Actions(Actions.SELECT_ALL));
1606 map.put(new Actions(Actions.CLEAR_SELECTION));
1607 map.put(new Actions(Actions.CANCEL_EDITING));
1608 map.put(new Actions(Actions.START_EDITING));
1609
1610 map.put(TransferHandler.getCutAction().getValue(Action.NAME),
1611 TransferHandler.getCutAction());
1612 map.put(TransferHandler.getCopyAction().getValue(Action.NAME),
1613 TransferHandler.getCopyAction());
1614 map.put(TransferHandler.getPasteAction().getValue(Action.NAME),
1615 TransferHandler.getPasteAction());
1616
1617 map.put(new Actions(Actions.SCROLL_LEFT_CHANGE_SELECTION,
1618 false, false, false, false));
1619 map.put(new Actions(Actions.SCROLL_RIGHT_CHANGE_SELECTION,
1620 false, true, false, false));
1621 map.put(new Actions(Actions.SCROLL_LEFT_EXTEND_SELECTION, true,
1622 false, false, false));
1623 map.put(new Actions(Actions.SCROLL_RIGHT_EXTEND_SELECTION,
1624 true, true, false, false));
1625
1626 map.put(new Actions(Actions.ADD_TO_SELECTION));
1627 map.put(new Actions(Actions.TOGGLE_AND_ANCHOR));
1628 map.put(new Actions(Actions.EXTEND_TO));
1629 map.put(new Actions(Actions.MOVE_SELECTION_TO));
1630 map.put(new Actions(Actions.FOCUS_HEADER));
1631 }
1632
1633 // Uninstallation
1634
1635 public void uninstallUI(JComponent c) {
1636 uninstallDefaults();
1637 uninstallListeners();
1638 uninstallKeyboardActions();
1639
1640 table.remove(rendererPane);
1641 rendererPane = null;
1642 table = null;
1643 }
1644
1645 protected void uninstallDefaults() {
1646 if (table.getTransferHandler() instanceof UIResource) {
1647 table.setTransferHandler(null);
1648 }
1649 }
1650
1651 protected void uninstallListeners() {
1652 table.removeFocusListener(focusListener);
1653 table.removeKeyListener(keyListener);
1654 table.removeMouseListener(mouseInputListener);
1655 table.removeMouseMotionListener(mouseInputListener);
1656 table.removePropertyChangeListener(getHandler());
1657 if (isFileList) {
1658 table.getSelectionModel().removeListSelectionListener(
1659 getHandler());
1660 }
1661
1662 focusListener = null;
1663 keyListener = null;
1664 mouseInputListener = null;
1665 handler = null;
1666 }
1667
1668 protected void uninstallKeyboardActions() {
1669 SwingUtilities.replaceUIInputMap(table,
1670 JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null);
1671 SwingUtilities.replaceUIActionMap(table, null);
1672 }
1673
1674 /**
1675 * Returns the baseline.
1676 *
1677 * @throws NullPointerException {@inheritDoc}
1678 * @throws IllegalArgumentException {@inheritDoc}
1679 * @see javax.swing.JComponent#getBaseline(int, int)
1680 * @since 1.6
1681 */
1682 public int getBaseline(JComponent c, int width, int height) {
1683 super .getBaseline(c, width, height);
1684 UIDefaults lafDefaults = UIManager.getLookAndFeelDefaults();
1685 Component renderer = (Component) lafDefaults
1686 .get(BASELINE_COMPONENT_KEY);
1687 if (renderer == null) {
1688 DefaultTableCellRenderer tcr = new DefaultTableCellRenderer();
1689 renderer = tcr.getTableCellRendererComponent(table, "a",
1690 false, false, -1, -1);
1691 lafDefaults.put(BASELINE_COMPONENT_KEY, renderer);
1692 }
1693 renderer.setFont(table.getFont());
1694 int rowMargin = table.getRowMargin();
1695 return renderer.getBaseline(Integer.MAX_VALUE, table
1696 .getRowHeight()
1697 - rowMargin)
1698 + rowMargin / 2;
1699 }
1700
1701 /**
1702 * Returns an enum indicating how the baseline of the component
1703 * changes as the size changes.
1704 *
1705 * @throws NullPointerException {@inheritDoc}
1706 * @see javax.swing.JComponent#getBaseline(int, int)
1707 * @since 1.6
1708 */
1709 public Component.BaselineResizeBehavior getBaselineResizeBehavior(
1710 JComponent c) {
1711 super .getBaselineResizeBehavior(c);
1712 return Component.BaselineResizeBehavior.CONSTANT_ASCENT;
1713 }
1714
1715 //
1716 // Size Methods
1717 //
1718
1719 private Dimension createTableSize(long width) {
1720 int height = 0;
1721 int rowCount = table.getRowCount();
1722 if (rowCount > 0 && table.getColumnCount() > 0) {
1723 Rectangle r = table.getCellRect(rowCount - 1, 0, true);
1724 height = r.y + r.height;
1725 }
1726 // Width is always positive. The call to abs() is a workaround for
1727 // a bug in the 1.1.6 JIT on Windows.
1728 long tmp = Math.abs(width);
1729 if (tmp > Integer.MAX_VALUE) {
1730 tmp = Integer.MAX_VALUE;
1731 }
1732 return new Dimension((int) tmp, height);
1733 }
1734
1735 /**
1736 * Return the minimum size of the table. The minimum height is the
1737 * row height times the number of rows.
1738 * The minimum width is the sum of the minimum widths of each column.
1739 */
1740 public Dimension getMinimumSize(JComponent c) {
1741 long width = 0;
1742 Enumeration enumeration = table.getColumnModel().getColumns();
1743 while (enumeration.hasMoreElements()) {
1744 TableColumn aColumn = (TableColumn) enumeration
1745 .nextElement();
1746 width = width + aColumn.getMinWidth();
1747 }
1748 return createTableSize(width);
1749 }
1750
1751 /**
1752 * Return the preferred size of the table. The preferred height is the
1753 * row height times the number of rows.
1754 * The preferred width is the sum of the preferred widths of each column.
1755 */
1756 public Dimension getPreferredSize(JComponent c) {
1757 long width = 0;
1758 Enumeration enumeration = table.getColumnModel().getColumns();
1759 while (enumeration.hasMoreElements()) {
1760 TableColumn aColumn = (TableColumn) enumeration
1761 .nextElement();
1762 width = width + aColumn.getPreferredWidth();
1763 }
1764 return createTableSize(width);
1765 }
1766
1767 /**
1768 * Return the maximum size of the table. The maximum height is the
1769 * row heighttimes the number of rows.
1770 * The maximum width is the sum of the maximum widths of each column.
1771 */
1772 public Dimension getMaximumSize(JComponent c) {
1773 long width = 0;
1774 Enumeration enumeration = table.getColumnModel().getColumns();
1775 while (enumeration.hasMoreElements()) {
1776 TableColumn aColumn = (TableColumn) enumeration
1777 .nextElement();
1778 width = width + aColumn.getMaxWidth();
1779 }
1780 return createTableSize(width);
1781 }
1782
1783 //
1784 // Paint methods and support
1785 //
1786
1787 /** Paint a representation of the <code>table</code> instance
1788 * that was set in installUI().
1789 */
1790 public void paint(Graphics g, JComponent c) {
1791 Rectangle clip = g.getClipBounds();
1792
1793 Rectangle bounds = table.getBounds();
1794 // account for the fact that the graphics has already been translated
1795 // into the table's bounds
1796 bounds.x = bounds.y = 0;
1797
1798 if (table.getRowCount() <= 0 || table.getColumnCount() <= 0 ||
1799 // this check prevents us from painting the entire table
1800 // when the clip doesn't intersect our bounds at all
1801 !bounds.intersects(clip)) {
1802
1803 paintDropLines(g);
1804 return;
1805 }
1806
1807 boolean ltr = table.getComponentOrientation().isLeftToRight();
1808
1809 Point upperLeft = clip.getLocation();
1810 Point lowerRight = new Point(clip.x + clip.width - 1, clip.y
1811 + clip.height - 1);
1812
1813 int rMin = table.rowAtPoint(upperLeft);
1814 int rMax = table.rowAtPoint(lowerRight);
1815 // This should never happen (as long as our bounds intersect the clip,
1816 // which is why we bail above if that is the case).
1817 if (rMin == -1) {
1818 rMin = 0;
1819 }
1820 // If the table does not have enough rows to fill the view we'll get -1.
1821 // (We could also get -1 if our bounds don't intersect the clip,
1822 // which is why we bail above if that is the case).
1823 // Replace this with the index of the last row.
1824 if (rMax == -1) {
1825 rMax = table.getRowCount() - 1;
1826 }
1827
1828 int cMin = table.columnAtPoint(ltr ? upperLeft : lowerRight);
1829 int cMax = table.columnAtPoint(ltr ? lowerRight : upperLeft);
1830 // This should never happen.
1831 if (cMin == -1) {
1832 cMin = 0;
1833 }
1834 // If the table does not have enough columns to fill the view we'll get -1.
1835 // Replace this with the index of the last column.
1836 if (cMax == -1) {
1837 cMax = table.getColumnCount() - 1;
1838 }
1839
1840 // Paint the grid.
1841 paintGrid(g, rMin, rMax, cMin, cMax);
1842
1843 // Paint the cells.
1844 paintCells(g, rMin, rMax, cMin, cMax);
1845
1846 paintDropLines(g);
1847 }
1848
1849 private void paintDropLines(Graphics g) {
1850 JTable.DropLocation loc = table.getDropLocation();
1851 if (loc == null) {
1852 return;
1853 }
1854
1855 Color color = UIManager.getColor("Table.dropLineColor");
1856 Color shortColor = UIManager
1857 .getColor("Table.dropLineShortColor");
1858 if (color == null && shortColor == null) {
1859 return;
1860 }
1861
1862 Rectangle rect;
1863
1864 rect = getHDropLineRect(loc);
1865 if (rect != null) {
1866 int x = rect.x;
1867 int w = rect.width;
1868 if (color != null) {
1869 extendRect(rect, true);
1870 g.setColor(color);
1871 g.fillRect(rect.x, rect.y, rect.width, rect.height);
1872 }
1873 if (!loc.isInsertColumn() && shortColor != null) {
1874 g.setColor(shortColor);
1875 g.fillRect(x, rect.y, w, rect.height);
1876 }
1877 }
1878
1879 rect = getVDropLineRect(loc);
1880 if (rect != null) {
1881 int y = rect.y;
1882 int h = rect.height;
1883 if (color != null) {
1884 extendRect(rect, false);
1885 g.setColor(color);
1886 g.fillRect(rect.x, rect.y, rect.width, rect.height);
1887 }
1888 if (!loc.isInsertRow() && shortColor != null) {
1889 g.setColor(shortColor);
1890 g.fillRect(rect.x, y, rect.width, h);
1891 }
1892 }
1893 }
1894
1895 private Rectangle getHDropLineRect(JTable.DropLocation loc) {
1896 if (!loc.isInsertRow()) {
1897 return null;
1898 }
1899
1900 int row = loc.getRow();
1901 int col = loc.getColumn();
1902 if (col >= table.getColumnCount()) {
1903 col--;
1904 }
1905
1906 Rectangle rect = table.getCellRect(row, col, true);
1907
1908 if (row >= table.getRowCount()) {
1909 row--;
1910 Rectangle prevRect = table.getCellRect(row, col, true);
1911 rect.y = prevRect.y + prevRect.height;
1912 }
1913
1914 if (rect.y == 0) {
1915 rect.y = -1;
1916 } else {
1917 rect.y -= 2;
1918 }
1919
1920 rect.height = 3;
1921
1922 return rect;
1923 }
1924
1925 private Rectangle getVDropLineRect(JTable.DropLocation loc) {
1926 if (!loc.isInsertColumn()) {
1927 return null;
1928 }
1929
1930 boolean ltr = table.getComponentOrientation().isLeftToRight();
1931 int col = loc.getColumn();
1932 Rectangle rect = table.getCellRect(loc.getRow(), col, true);
1933
1934 if (col >= table.getColumnCount()) {
1935 col--;
1936 rect = table.getCellRect(loc.getRow(), col, true);
1937 if (ltr) {
1938 rect.x = rect.x + rect.width;
1939 }
1940 } else if (!ltr) {
1941 rect.x = rect.x + rect.width;
1942 }
1943
1944 if (rect.x == 0) {
1945 rect.x = -1;
1946 } else {
1947 rect.x -= 2;
1948 }
1949
1950 rect.width = 3;
1951
1952 return rect;
1953 }
1954
1955 private Rectangle extendRect(Rectangle rect, boolean horizontal) {
1956 if (rect == null) {
1957 return rect;
1958 }
1959
1960 if (horizontal) {
1961 rect.x = 0;
1962 rect.width = table.getWidth();
1963 } else {
1964 rect.y = 0;
1965
1966 if (table.getRowCount() != 0) {
1967 Rectangle lastRect = table.getCellRect(table
1968 .getRowCount() - 1, 0, true);
1969 rect.height = lastRect.y + lastRect.height;
1970 } else {
1971 rect.height = table.getHeight();
1972 }
1973 }
1974
1975 return rect;
1976 }
1977
1978 /*
1979 * Paints the grid lines within <I>aRect</I>, using the grid
1980 * color set with <I>setGridColor</I>. Paints vertical lines
1981 * if <code>getShowVerticalLines()</code> returns true and paints
1982 * horizontal lines if <code>getShowHorizontalLines()</code>
1983 * returns true.
1984 */
1985 private void paintGrid(Graphics g, int rMin, int rMax, int cMin,
1986 int cMax) {
1987 g.setColor(table.getGridColor());
1988
1989 Rectangle minCell = table.getCellRect(rMin, cMin, true);
1990 Rectangle maxCell = table.getCellRect(rMax, cMax, true);
1991 Rectangle damagedArea = minCell.union(maxCell);
1992
1993 if (table.getShowHorizontalLines()) {
1994 int tableWidth = damagedArea.x + damagedArea.width;
1995 int y = damagedArea.y;
1996 for (int row = rMin; row <= rMax; row++) {
1997 y += table.getRowHeight(row);
1998 g.drawLine(damagedArea.x, y - 1, tableWidth - 1, y - 1);
1999 }
2000 }
2001 if (table.getShowVerticalLines()) {
2002 TableColumnModel cm = table.getColumnModel();
2003 int tableHeight = damagedArea.y + damagedArea.height;
2004 int x;
2005 if (table.getComponentOrientation().isLeftToRight()) {
2006 x = damagedArea.x;
2007 for (int column = cMin; column <= cMax; column++) {
2008 int w = cm.getColumn(column).getWidth();
2009 x += w;
2010 g.drawLine(x - 1, 0, x - 1, tableHeight - 1);
2011 }
2012 } else {
2013 x = damagedArea.x;
2014 for (int column = cMax; column >= cMin; column--) {
2015 int w = cm.getColumn(column).getWidth();
2016 x += w;
2017 g.drawLine(x - 1, 0, x - 1, tableHeight - 1);
2018 }
2019 }
2020 }
2021 }
2022
2023 private int viewIndexForColumn(TableColumn aColumn) {
2024 TableColumnModel cm = table.getColumnModel();
2025 for (int column = 0; column < cm.getColumnCount(); column++) {
2026 if (cm.getColumn(column) == aColumn) {
2027 return column;
2028 }
2029 }
2030 return -1;
2031 }
2032
2033 private void paintCells(Graphics g, int rMin, int rMax, int cMin,
2034 int cMax) {
2035 JTableHeader header = table.getTableHeader();
2036 TableColumn draggedColumn = (header == null) ? null : header
2037 .getDraggedColumn();
2038
2039 TableColumnModel cm = table.getColumnModel();
2040 int columnMargin = cm.getColumnMargin();
2041
2042 Rectangle cellRect;
2043 TableColumn aColumn;
2044 int columnWidth;
2045 if (table.getComponentOrientation().isLeftToRight()) {
2046 for (int row = rMin; row <= rMax; row++) {
2047 cellRect = table.getCellRect(row, cMin, false);
2048 for (int column = cMin; column <= cMax; column++) {
2049 aColumn = cm.getColumn(column);
2050 columnWidth = aColumn.getWidth();
2051 cellRect.width = columnWidth - columnMargin;
2052 if (aColumn != draggedColumn) {
2053 paintCell(g, cellRect, row, column);
2054 }
2055 cellRect.x += columnWidth;
2056 }
2057 }
2058 } else {
2059 for (int row = rMin; row <= rMax; row++) {
2060 cellRect = table.getCellRect(row, cMin, false);
2061 aColumn = cm.getColumn(cMin);
2062 if (aColumn != draggedColumn) {
2063 columnWidth = aColumn.getWidth();
2064 cellRect.width = columnWidth - columnMargin;
2065 paintCell(g, cellRect, row, cMin);
2066 }
2067 for (int column = cMin + 1; column <= cMax; column++) {
2068 aColumn = cm.getColumn(column);
2069 columnWidth = aColumn.getWidth();
2070 cellRect.width = columnWidth - columnMargin;
2071 cellRect.x -= columnWidth;
2072 if (aColumn != draggedColumn) {
2073 paintCell(g, cellRect, row, column);
2074 }
2075 }
2076 }
2077 }
2078
2079 // Paint the dragged column if we are dragging.
2080 if (draggedColumn != null) {
2081 paintDraggedArea(g, rMin, rMax, draggedColumn, header
2082 .getDraggedDistance());
2083 }
2084
2085 // Remove any renderers that may be left in the rendererPane.
2086 rendererPane.removeAll();
2087 }
2088
2089 private void paintDraggedArea(Graphics g, int rMin, int rMax,
2090 TableColumn draggedColumn, int distance) {
2091 int draggedColumnIndex = viewIndexForColumn(draggedColumn);
2092
2093 Rectangle minCell = table.getCellRect(rMin, draggedColumnIndex,
2094 true);
2095 Rectangle maxCell = table.getCellRect(rMax, draggedColumnIndex,
2096 true);
2097
2098 Rectangle vacatedColumnRect = minCell.union(maxCell);
2099
2100 // Paint a gray well in place of the moving column.
2101 g.setColor(table.getParent().getBackground());
2102 g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
2103 vacatedColumnRect.width, vacatedColumnRect.height);
2104
2105 // Move to the where the cell has been dragged.
2106 vacatedColumnRect.x += distance;
2107
2108 // Fill the background.
2109 g.setColor(table.getBackground());
2110 g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
2111 vacatedColumnRect.width, vacatedColumnRect.height);
2112
2113 // Paint the vertical grid lines if necessary.
2114 if (table.getShowVerticalLines()) {
2115 g.setColor(table.getGridColor());
2116 int x1 = vacatedColumnRect.x;
2117 int y1 = vacatedColumnRect.y;
2118 int x2 = x1 + vacatedColumnRect.width - 1;
2119 int y2 = y1 + vacatedColumnRect.height - 1;
2120 // Left
2121 g.drawLine(x1 - 1, y1, x1 - 1, y2);
2122 // Right
2123 g.drawLine(x2, y1, x2, y2);
2124 }
2125
2126 for (int row = rMin; row <= rMax; row++) {
2127 // Render the cell value
2128 Rectangle r = table.getCellRect(row, draggedColumnIndex,
2129 false);
2130 r.x += distance;
2131 paintCell(g, r, row, draggedColumnIndex);
2132
2133 // Paint the (lower) horizontal grid line if necessary.
2134 if (table.getShowHorizontalLines()) {
2135 g.setColor(table.getGridColor());
2136 Rectangle rcr = table.getCellRect(row,
2137 draggedColumnIndex, true);
2138 rcr.x += distance;
2139 int x1 = rcr.x;
2140 int y1 = rcr.y;
2141 int x2 = x1 + rcr.width - 1;
2142 int y2 = y1 + rcr.height - 1;
2143 g.drawLine(x1, y2, x2, y2);
2144 }
2145 }
2146 }
2147
2148 private void paintCell(Graphics g, Rectangle cellRect, int row,
2149 int column) {
2150 if (table.isEditing() && table.getEditingRow() == row
2151 && table.getEditingColumn() == column) {
2152 Component component = table.getEditorComponent();
2153 component.setBounds(cellRect);
2154 component.validate();
2155 } else {
2156 TableCellRenderer renderer = table.getCellRenderer(row,
2157 column);
2158 Component component = table.prepareRenderer(renderer, row,
2159 column);
2160 rendererPane.paintComponent(g, component, table,
2161 cellRect.x, cellRect.y, cellRect.width,
2162 cellRect.height, true);
2163 }
2164 }
2165
2166 private static int getAdjustedLead(JTable table, boolean row,
2167 ListSelectionModel model) {
2168
2169 int index = model.getLeadSelectionIndex();
2170 int compare = row ? table.getRowCount() : table
2171 .getColumnCount();
2172 return index < compare ? index : -1;
2173 }
2174
2175 private static int getAdjustedLead(JTable table, boolean row) {
2176 return row ? getAdjustedLead(table, row, table
2177 .getSelectionModel()) : getAdjustedLead(table, row,
2178 table.getColumnModel().getSelectionModel());
2179 }
2180
2181 private static final TransferHandler defaultTransferHandler = new TableTransferHandler();
2182
2183 static class TableTransferHandler extends TransferHandler implements
2184 UIResource {
2185
2186 /**
2187 * Create a Transferable to use as the source for a data transfer.
2188 *
2189 * @param c The component holding the data to be transfered. This
2190 * argument is provided to enable sharing of TransferHandlers by
2191 * multiple components.
2192 * @return The representation of the data to be transfered.
2193 *
2194 */
2195 protected Transferable createTransferable(JComponent c) {
2196 if (c instanceof JTable) {
2197 JTable table = (JTable) c;
2198 int[] rows;
2199 int[] cols;
2200
2201 if (!table.getRowSelectionAllowed()
2202 && !table.getColumnSelectionAllowed()) {
2203 return null;
2204 }
2205
2206 if (!table.getRowSelectionAllowed()) {
2207 int rowCount = table.getRowCount();
2208
2209 rows = new int[rowCount];
2210 for (int counter = 0; counter < rowCount; counter++) {
2211 rows[counter] = counter;
2212 }
2213 } else {
2214 rows = table.getSelectedRows();
2215 }
2216
2217 if (!table.getColumnSelectionAllowed()) {
2218 int colCount = table.getColumnCount();
2219
2220 cols = new int[colCount];
2221 for (int counter = 0; counter < colCount; counter++) {
2222 cols[counter] = counter;
2223 }
2224 } else {
2225 cols = table.getSelectedColumns();
2226 }
2227
2228 if (rows == null || cols == null || rows.length == 0
2229 || cols.length == 0) {
2230 return null;
2231 }
2232
2233 StringBuffer plainBuf = new StringBuffer();
2234 StringBuffer htmlBuf = new StringBuffer();
2235
2236 htmlBuf.append("<html>\n<body>\n<table>\n");
2237
2238 for (int row = 0; row < rows.length; row++) {
2239 htmlBuf.append("<tr>\n");
2240 for (int col = 0; col < cols.length; col++) {
2241 Object obj = table.getValueAt(rows[row],
2242 cols[col]);
2243 String val = ((obj == null) ? "" : obj
2244 .toString());
2245 plainBuf.append(val + "\t");
2246 htmlBuf.append(" <td>" + val + "</td>\n");
2247 }
2248 // we want a newline at the end of each line and not a tab
2249 plainBuf.deleteCharAt(plainBuf.length() - 1)
2250 .append("\n");
2251 htmlBuf.append("</tr>\n");
2252 }
2253
2254 // remove the last newline
2255 plainBuf.deleteCharAt(plainBuf.length() - 1);
2256 htmlBuf.append("</table>\n</body>\n</html>");
2257
2258 return new BasicTransferable(plainBuf.toString(),
2259 htmlBuf.toString());
2260 }
2261
2262 return null;
2263 }
2264
2265 public int getSourceActions(JComponent c) {
2266 return COPY;
2267 }
2268
2269 }
2270 } // End of Class BasicTableUI
|