| /* BasicTableUI.java -- |
| Copyright (C) 2004 Free Software Foundation, Inc. |
| |
| This file is part of GNU Classpath. |
| |
| GNU Classpath is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 2, or (at your option) |
| any later version. |
| |
| GNU Classpath is distributed in the hope that it will be useful, but |
| WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GNU Classpath; see the file COPYING. If not, write to the |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
| 02110-1301 USA. |
| |
| Linking this library statically or dynamically with other modules is |
| making a combined work based on this library. Thus, the terms and |
| conditions of the GNU General Public License cover the whole |
| combination. |
| |
| As a special exception, the copyright holders of this library give you |
| permission to link this library with independent modules to produce an |
| executable, regardless of the license terms of these independent |
| modules, and to copy and distribute the resulting executable under |
| terms of your choice, provided that you also meet, for each linked |
| independent module, the terms and conditions of the license of that |
| module. An independent module is a module which is not derived from |
| or based on this library. If you modify this library, you may extend |
| this exception to your version of the library, but you are not |
| obligated to do so. If you do not wish to do so, delete this |
| exception statement from your version. */ |
| |
| |
| package javax.swing.plaf.basic; |
| |
| import java.awt.Color; |
| import java.awt.Component; |
| import java.awt.ComponentOrientation; |
| import java.awt.Dimension; |
| import java.awt.Graphics; |
| import java.awt.Point; |
| import java.awt.Rectangle; |
| import java.awt.event.ActionEvent; |
| import java.awt.event.FocusEvent; |
| import java.awt.event.FocusListener; |
| import java.awt.event.KeyEvent; |
| import java.awt.event.KeyListener; |
| import java.awt.event.MouseEvent; |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| |
| import javax.swing.AbstractAction; |
| import javax.swing.Action; |
| import javax.swing.ActionMap; |
| import javax.swing.CellRendererPane; |
| import javax.swing.DefaultCellEditor; |
| import javax.swing.DefaultListSelectionModel; |
| import javax.swing.InputMap; |
| import javax.swing.JComponent; |
| import javax.swing.JTable; |
| import javax.swing.ListSelectionModel; |
| import javax.swing.LookAndFeel; |
| import javax.swing.SwingUtilities; |
| import javax.swing.TransferHandler; |
| import javax.swing.UIManager; |
| import javax.swing.border.Border; |
| import javax.swing.event.ChangeEvent; |
| import javax.swing.event.MouseInputListener; |
| import javax.swing.plaf.ActionMapUIResource; |
| import javax.swing.plaf.ComponentUI; |
| import javax.swing.plaf.TableUI; |
| import javax.swing.table.TableCellEditor; |
| import javax.swing.table.TableCellRenderer; |
| import javax.swing.table.TableColumn; |
| import javax.swing.table.TableColumnModel; |
| import javax.swing.table.TableModel; |
| |
| public class BasicTableUI extends TableUI |
| { |
| public static ComponentUI createUI(JComponent comp) |
| { |
| return new BasicTableUI(); |
| } |
| |
| protected FocusListener focusListener; |
| protected KeyListener keyListener; |
| protected MouseInputListener mouseInputListener; |
| protected CellRendererPane rendererPane; |
| protected JTable table; |
| |
| /** The normal cell border. */ |
| Border cellBorder; |
| |
| /** The action bound to KeyStrokes. */ |
| TableAction action; |
| |
| /** |
| * Listens for changes to the tables properties. |
| */ |
| private PropertyChangeListener propertyChangeListener; |
| |
| /** |
| * Handles key events for the JTable. Key events should be handled through |
| * the InputMap/ActionMap mechanism since JDK1.3. This class is only there |
| * for backwards compatibility. |
| * |
| * @author Roman Kennke (kennke@aicas.com) |
| */ |
| public class KeyHandler implements KeyListener |
| { |
| |
| /** |
| * Receives notification that a key has been pressed and released. |
| * Activates the editing session for the focused cell by pressing the |
| * character keys. |
| * |
| * @param event the key event |
| */ |
| public void keyTyped(KeyEvent event) |
| { |
| // Key events should be handled through the InputMap/ActionMap mechanism |
| // since JDK1.3. This class is only there for backwards compatibility. |
| |
| // Editor activation is a specific kind of response to ''any'' |
| // character key. Hence it is handled here. |
| if (!table.isEditing() && table.isEnabled()) |
| { |
| int r = table.getSelectedRow(); |
| int c = table.getSelectedColumn(); |
| if (table.isCellEditable(r, c)) |
| table.editCellAt(r, c); |
| } |
| } |
| |
| /** |
| * Receives notification that a key has been pressed. |
| * |
| * @param event the key event |
| */ |
| public void keyPressed(KeyEvent event) |
| { |
| // Key events should be handled through the InputMap/ActionMap mechanism |
| // since JDK1.3. This class is only there for backwards compatibility. |
| } |
| |
| /** |
| * Receives notification that a key has been released. |
| * |
| * @param event the key event |
| */ |
| public void keyReleased(KeyEvent event) |
| { |
| // Key events should be handled through the InputMap/ActionMap mechanism |
| // since JDK1.3. This class is only there for backwards compatibility. |
| } |
| } |
| |
| public class FocusHandler implements FocusListener |
| { |
| public void focusGained(FocusEvent e) |
| { |
| // The only thing that is affected by a focus change seems to be |
| // how the lead cell is painted. So we repaint this cell. |
| repaintLeadCell(); |
| } |
| |
| public void focusLost(FocusEvent e) |
| { |
| // The only thing that is affected by a focus change seems to be |
| // how the lead cell is painted. So we repaint this cell. |
| repaintLeadCell(); |
| } |
| |
| /** |
| * Repaints the lead cell in response to a focus change, to refresh |
| * the display of the focus indicator. |
| */ |
| private void repaintLeadCell() |
| { |
| int rowCount = table.getRowCount(); |
| int columnCount = table.getColumnCount(); |
| int rowLead = table.getSelectionModel().getLeadSelectionIndex(); |
| int columnLead = table.getColumnModel().getSelectionModel(). |
| getLeadSelectionIndex(); |
| if (rowLead >= 0 && rowLead < rowCount && columnLead >= 0 |
| && columnLead < columnCount) |
| { |
| Rectangle dirtyRect = table.getCellRect(rowLead, columnLead, false); |
| table.repaint(dirtyRect); |
| } |
| } |
| } |
| |
| public class MouseInputHandler implements MouseInputListener |
| { |
| Point begin, curr; |
| |
| private void updateSelection(boolean controlPressed) |
| { |
| // Update the rows |
| int lo_row = table.rowAtPoint(begin); |
| int hi_row = table.rowAtPoint(curr); |
| ListSelectionModel rowModel = table.getSelectionModel(); |
| if (lo_row != -1 && hi_row != -1) |
| { |
| if (controlPressed && rowModel.getSelectionMode() |
| != ListSelectionModel.SINGLE_SELECTION) |
| rowModel.addSelectionInterval(lo_row, hi_row); |
| else |
| rowModel.setSelectionInterval(lo_row, hi_row); |
| } |
| |
| // Update the columns |
| int lo_col = table.columnAtPoint(begin); |
| int hi_col = table.columnAtPoint(curr); |
| ListSelectionModel colModel = table.getColumnModel(). |
| getSelectionModel(); |
| if (lo_col != -1 && hi_col != -1) |
| { |
| if (controlPressed && colModel.getSelectionMode() != |
| ListSelectionModel.SINGLE_SELECTION) |
| colModel.addSelectionInterval(lo_col, hi_col); |
| else |
| colModel.setSelectionInterval(lo_col, hi_col); |
| } |
| } |
| |
| /** |
| * For the double click, start the cell editor. |
| */ |
| public void mouseClicked(MouseEvent e) |
| { |
| Point p = e.getPoint(); |
| int row = table.rowAtPoint(p); |
| int col = table.columnAtPoint(p); |
| if (table.isCellEditable(row, col)) |
| { |
| // If the cell editor is the default editor, we request the |
| // number of the required clicks from it. Otherwise, |
| // require two clicks (double click). |
| TableCellEditor editor = table.getCellEditor(row, col); |
| if (editor instanceof DefaultCellEditor) |
| { |
| DefaultCellEditor ce = (DefaultCellEditor) editor; |
| if (e.getClickCount() < ce.getClickCountToStart()) |
| return; |
| } |
| table.editCellAt(row, col); |
| } |
| } |
| |
| public void mouseDragged(MouseEvent e) |
| { |
| if (table.isEnabled()) |
| { |
| curr = new Point(e.getX(), e.getY()); |
| updateSelection(e.isControlDown()); |
| } |
| } |
| |
| public void mouseEntered(MouseEvent e) |
| { |
| // Nothing to do here. |
| } |
| |
| public void mouseExited(MouseEvent e) |
| { |
| // Nothing to do here. |
| } |
| |
| public void mouseMoved(MouseEvent e) |
| { |
| // Nothing to do here. |
| } |
| |
| public void mousePressed(MouseEvent e) |
| { |
| if (table.isEnabled()) |
| { |
| ListSelectionModel rowModel = table.getSelectionModel(); |
| ListSelectionModel colModel = table.getColumnModel().getSelectionModel(); |
| int rowLead = rowModel.getLeadSelectionIndex(); |
| int colLead = colModel.getLeadSelectionIndex(); |
| |
| begin = new Point(e.getX(), e.getY()); |
| curr = new Point(e.getX(), e.getY()); |
| //if control is pressed and the cell is already selected, deselect it |
| if (e.isControlDown() && table.isCellSelected( |
| table.rowAtPoint(begin), table.columnAtPoint(begin))) |
| { |
| table.getSelectionModel(). |
| removeSelectionInterval(table.rowAtPoint(begin), |
| table.rowAtPoint(begin)); |
| table.getColumnModel().getSelectionModel(). |
| removeSelectionInterval(table.columnAtPoint(begin), |
| table.columnAtPoint(begin)); |
| } |
| else |
| updateSelection(e.isControlDown()); |
| |
| // If we were editing, but the moved to another cell, stop editing |
| if (rowLead != rowModel.getLeadSelectionIndex() || |
| colLead != colModel.getLeadSelectionIndex()) |
| if (table.isEditing()) |
| table.editingStopped(new ChangeEvent(e)); |
| |
| // Must request focus explicitly. |
| table.requestFocusInWindow(); |
| } |
| } |
| |
| public void mouseReleased(MouseEvent e) |
| { |
| if (table.isEnabled()) |
| { |
| begin = null; |
| curr = null; |
| } |
| } |
| } |
| |
| /** |
| * Listens for changes to the model property of the JTable and adjusts some |
| * settings. |
| * |
| * @author Roman Kennke (kennke@aicas.com) |
| */ |
| private class PropertyChangeHandler implements PropertyChangeListener |
| { |
| /** |
| * Receives notification if one of the JTable's properties changes. |
| * |
| * @param ev the property change event |
| */ |
| public void propertyChange(PropertyChangeEvent ev) |
| { |
| String propName = ev.getPropertyName(); |
| if (propName.equals("model")) |
| { |
| ListSelectionModel rowSel = table.getSelectionModel(); |
| rowSel.clearSelection(); |
| ListSelectionModel colSel = table.getColumnModel().getSelectionModel(); |
| colSel.clearSelection(); |
| TableModel model = table.getModel(); |
| |
| // Adjust lead and anchor selection indices of the row and column |
| // selection models. |
| if (model.getRowCount() > 0) |
| { |
| rowSel.setAnchorSelectionIndex(0); |
| rowSel.setLeadSelectionIndex(0); |
| } |
| else |
| { |
| rowSel.setAnchorSelectionIndex(-1); |
| rowSel.setLeadSelectionIndex(-1); |
| } |
| if (model.getColumnCount() > 0) |
| { |
| colSel.setAnchorSelectionIndex(0); |
| colSel.setLeadSelectionIndex(0); |
| } |
| else |
| { |
| colSel.setAnchorSelectionIndex(-1); |
| colSel.setLeadSelectionIndex(-1); |
| } |
| } |
| } |
| } |
| |
| protected FocusListener createFocusListener() |
| { |
| return new FocusHandler(); |
| } |
| |
| protected MouseInputListener createMouseInputListener() |
| { |
| return new MouseInputHandler(); |
| } |
| |
| |
| /** |
| * Creates and returns a key listener for the JTable. |
| * |
| * @return a key listener for the JTable |
| */ |
| protected KeyListener createKeyListener() |
| { |
| return new KeyHandler(); |
| } |
| |
| /** |
| * Return the maximum size of the table. The maximum height is the row |
| * height times the number of rows. The maximum width is the sum of |
| * the maximum widths of each column. |
| * |
| * @param comp the component whose maximum size is being queried, |
| * this is ignored. |
| * @return a Dimension object representing the maximum size of the table, |
| * or null if the table has no elements. |
| */ |
| public Dimension getMaximumSize(JComponent comp) |
| { |
| int maxTotalColumnWidth = 0; |
| for (int i = 0; i < table.getColumnCount(); i++) |
| maxTotalColumnWidth += table.getColumnModel().getColumn(i).getMaxWidth(); |
| |
| return new Dimension(maxTotalColumnWidth, getHeight()); |
| } |
| |
| /** |
| * Return the minimum size of the table. The minimum height is the row |
| * height times the number of rows. The minimum width is the sum of |
| * the minimum widths of each column. |
| * |
| * @param comp the component whose minimum size is being queried, |
| * this is ignored. |
| * @return a Dimension object representing the minimum size of the table, |
| * or null if the table has no elements. |
| */ |
| public Dimension getMinimumSize(JComponent comp) |
| { |
| int minTotalColumnWidth = 0; |
| for (int i = 0; i < table.getColumnCount(); i++) |
| minTotalColumnWidth += table.getColumnModel().getColumn(i).getMinWidth(); |
| |
| return new Dimension(minTotalColumnWidth, getHeight()); |
| } |
| |
| /** |
| * Returns the preferred size for the table of that UI. |
| * |
| * @param comp ignored, the <code>table</code> field is used instead |
| * |
| * @return the preferred size for the table of that UI |
| */ |
| public Dimension getPreferredSize(JComponent comp) |
| { |
| int prefTotalColumnWidth = 0; |
| for (int i = 0; i < table.getColumnCount(); i++) |
| { |
| TableColumn col = table.getColumnModel().getColumn(i); |
| prefTotalColumnWidth += col.getPreferredWidth(); |
| } |
| return new Dimension(prefTotalColumnWidth, getHeight()); |
| } |
| |
| /** |
| * Returns the table height. This helper method is used by |
| * {@link #getMinimumSize(JComponent)}, {@link #getPreferredSize(JComponent)} |
| * and {@link #getMaximumSize(JComponent)} to determine the table height. |
| * |
| * @return the table height |
| */ |
| private int getHeight() |
| { |
| int height = 0; |
| int rowCount = table.getRowCount(); |
| if (rowCount > 0 && table.getColumnCount() > 0) |
| { |
| Rectangle r = table.getCellRect(rowCount - 1, 0, true); |
| height = r.y + r.height; |
| } |
| return height; |
| } |
| |
| protected void installDefaults() |
| { |
| LookAndFeel.installColorsAndFont(table, "Table.background", |
| "Table.foreground", "Table.font"); |
| table.setGridColor(UIManager.getColor("Table.gridColor")); |
| table.setSelectionForeground(UIManager.getColor("Table.selectionForeground")); |
| table.setSelectionBackground(UIManager.getColor("Table.selectionBackground")); |
| table.setOpaque(true); |
| } |
| |
| /** |
| * Installs keyboard actions on the table. |
| */ |
| protected void installKeyboardActions() |
| { |
| // Install the input map. |
| InputMap inputMap = |
| (InputMap) SharedUIDefaults.get("Table.ancestorInputMap"); |
| SwingUtilities.replaceUIInputMap(table, |
| JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, |
| inputMap); |
| |
| // FIXME: The JDK uses a LazyActionMap for parentActionMap |
| SwingUtilities.replaceUIActionMap(table, getActionMap()); |
| |
| } |
| |
| /** |
| * Fetches the action map from the UI defaults, or create a new one |
| * if the action map hasn't been initialized. |
| * |
| * @return the action map |
| */ |
| private ActionMap getActionMap() |
| { |
| ActionMap am = (ActionMap) UIManager.get("Table.actionMap"); |
| if (am == null) |
| { |
| am = createDefaultActions(); |
| UIManager.getLookAndFeelDefaults().put("Table.actionMap", am); |
| } |
| return am; |
| } |
| |
| private ActionMap createDefaultActions() |
| { |
| ActionMapUIResource am = new ActionMapUIResource(); |
| Action action = new TableAction(); |
| |
| am.put("cut", TransferHandler.getCutAction()); |
| am.put("copy", TransferHandler.getCopyAction()); |
| am.put("paste", TransferHandler.getPasteAction()); |
| |
| am.put("cancel", action); |
| am.put("selectAll", action); |
| am.put("clearSelection", action); |
| am.put("startEditing", action); |
| |
| am.put("selectNextRow", action); |
| am.put("selectNextRowCell", action); |
| am.put("selectNextRowExtendSelection", action); |
| am.put("selectNextRowChangeLead", action); |
| |
| am.put("selectPreviousRow", action); |
| am.put("selectPreviousRowCell", action); |
| am.put("selectPreviousRowExtendSelection", action); |
| am.put("selectPreviousRowChangeLead", action); |
| |
| am.put("selectNextColumn", action); |
| am.put("selectNextColumnCell", action); |
| am.put("selectNextColumnExtendSelection", action); |
| am.put("selectNextColumnChangeLead", action); |
| |
| am.put("selectPreviousColumn", action); |
| am.put("selectPreviousColumnCell", action); |
| am.put("selectPreviousColumnExtendSelection", action); |
| am.put("selectPreviousColumnChangeLead", action); |
| |
| am.put("scrollLeftChangeSelection", action); |
| am.put("scrollLeftExtendSelection", action); |
| am.put("scrollRightChangeSelection", action); |
| am.put("scrollRightExtendSelection", action); |
| |
| am.put("scrollUpChangeSelection", action); |
| am.put("scrollUpExtendSelection", action); |
| am.put("scrollDownChangeSelection", action); |
| am.put("scrolldownExtendSelection", action); |
| |
| am.put("selectFirstColumn", action); |
| am.put("selectFirstColumnExtendSelection", action); |
| am.put("selectLastColumn", action); |
| am.put("selectLastColumnExtendSelection", action); |
| |
| am.put("selectFirstRow", action); |
| am.put("selectFirstRowExtendSelection", action); |
| am.put("selectLastRow", action); |
| am.put("selectLastRowExtendSelection", action); |
| |
| am.put("addToSelection", action); |
| am.put("toggleAndAnchor", action); |
| am.put("extendTo", action); |
| am.put("moveSelectionTo", action); |
| |
| return am; |
| } |
| |
| /** |
| * This class implements the actions that we want to happen |
| * when specific keys are pressed for the JTable. The actionPerformed |
| * method is called when a key that has been registered for the JTable |
| * is received. |
| */ |
| private static class TableAction |
| extends AbstractAction |
| { |
| /** |
| * What to do when this action is called. |
| * |
| * @param e the ActionEvent that caused this action. |
| */ |
| public void actionPerformed(ActionEvent e) |
| { |
| JTable table = (JTable) e.getSource(); |
| |
| DefaultListSelectionModel rowModel |
| = (DefaultListSelectionModel) table.getSelectionModel(); |
| DefaultListSelectionModel colModel |
| = (DefaultListSelectionModel) table.getColumnModel().getSelectionModel(); |
| |
| int rowLead = rowModel.getLeadSelectionIndex(); |
| int rowMax = table.getModel().getRowCount() - 1; |
| |
| int colLead = colModel.getLeadSelectionIndex(); |
| int colMax = table.getModel().getColumnCount() - 1; |
| |
| // The command with which the action has been called is stored |
| // in this undocumented action value. This allows us to have only |
| // one Action instance to serve all keyboard input for JTable. |
| String command = (String) getValue("__command__"); |
| if (command.equals("selectPreviousRowExtendSelection")) |
| { |
| rowModel.setLeadSelectionIndex(Math.max(rowLead - 1, 0)); |
| } |
| else if (command.equals("selectLastColumn")) |
| { |
| colModel.setSelectionInterval(colMax, colMax); |
| } |
| else if (command.equals("startEditing")) |
| { |
| if (table.isCellEditable(rowLead, colLead)) |
| table.editCellAt(rowLead, colLead); |
| } |
| else if (command.equals("selectFirstRowExtendSelection")) |
| { |
| rowModel.setLeadSelectionIndex(0); |
| } |
| else if (command.equals("selectFirstColumn")) |
| { |
| colModel.setSelectionInterval(0, 0); |
| } |
| else if (command.equals("selectFirstColumnExtendSelection")) |
| { |
| colModel.setLeadSelectionIndex(0); |
| } |
| else if (command.equals("selectLastRow")) |
| { |
| rowModel.setSelectionInterval(rowMax, rowMax); |
| } |
| else if (command.equals("selectNextRowExtendSelection")) |
| { |
| rowModel.setLeadSelectionIndex(Math.min(rowLead + 1, rowMax)); |
| } |
| else if (command.equals("selectFirstRow")) |
| { |
| rowModel.setSelectionInterval(0, 0); |
| } |
| else if (command.equals("selectNextColumnExtendSelection")) |
| { |
| colModel.setLeadSelectionIndex(Math.min(colLead + 1, colMax)); |
| } |
| else if (command.equals("selectLastColumnExtendSelection")) |
| { |
| colModel.setLeadSelectionIndex(colMax); |
| } |
| else if (command.equals("selectPreviousColumnExtendSelection")) |
| { |
| colModel.setLeadSelectionIndex(Math.max(colLead - 1, 0)); |
| } |
| else if (command.equals("selectNextRow")) |
| { |
| rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax), |
| Math.min(rowLead + 1, rowMax)); |
| } |
| else if (command.equals("scrollUpExtendSelection")) |
| { |
| int target; |
| if (rowLead == getFirstVisibleRowIndex(table)) |
| target = Math.max(0, rowLead - (getLastVisibleRowIndex(table) |
| - getFirstVisibleRowIndex(table) + 1)); |
| else |
| target = getFirstVisibleRowIndex(table); |
| |
| rowModel.setLeadSelectionIndex(target); |
| colModel.setLeadSelectionIndex(colLead); |
| } |
| else if (command.equals("selectPreviousRow")) |
| { |
| rowModel.setSelectionInterval(Math.max(rowLead - 1, 0), |
| Math.max(rowLead - 1, 0)); |
| } |
| else if (command.equals("scrollRightChangeSelection")) |
| { |
| int target; |
| if (colLead == getLastVisibleColumnIndex(table)) |
| target = Math.min(colMax, colLead |
| + (getLastVisibleColumnIndex(table) |
| - getFirstVisibleColumnIndex(table) + 1)); |
| else |
| target = getLastVisibleColumnIndex(table); |
| |
| colModel.setSelectionInterval(target, target); |
| rowModel.setSelectionInterval(rowLead, rowLead); |
| } |
| else if (command.equals("selectPreviousColumn")) |
| { |
| colModel.setSelectionInterval(Math.max(colLead - 1, 0), |
| Math.max(colLead - 1, 0)); |
| } |
| else if (command.equals("scrollLeftChangeSelection")) |
| { |
| int target; |
| if (colLead == getFirstVisibleColumnIndex(table)) |
| target = Math.max(0, colLead - (getLastVisibleColumnIndex(table) |
| - getFirstVisibleColumnIndex(table) + 1)); |
| else |
| target = getFirstVisibleColumnIndex(table); |
| |
| colModel.setSelectionInterval(target, target); |
| rowModel.setSelectionInterval(rowLead, rowLead); |
| } |
| else if (command.equals("clearSelection")) |
| { |
| table.clearSelection(); |
| } |
| else if (command.equals("cancel")) |
| { |
| // FIXME: implement other parts of "cancel" like undo-ing last |
| // selection. Right now it just calls editingCancelled if |
| // we're currently editing. |
| if (table.isEditing()) |
| table.editingCanceled(new ChangeEvent("cancel")); |
| } |
| else if (command.equals("selectNextRowCell") |
| || command.equals("selectPreviousRowCell") |
| || command.equals("selectNextColumnCell") |
| || command.equals("selectPreviousColumnCell")) |
| { |
| // If nothing is selected, select the first cell in the table |
| if (table.getSelectedRowCount() == 0 && |
| table.getSelectedColumnCount() == 0) |
| { |
| rowModel.setSelectionInterval(0, 0); |
| colModel.setSelectionInterval(0, 0); |
| return; |
| } |
| |
| // If the lead selection index isn't selected (ie a remove operation |
| // happened, then set the lead to the first selected cell in the |
| // table |
| if (!table.isCellSelected(rowLead, colLead)) |
| { |
| rowModel.addSelectionInterval(rowModel.getMinSelectionIndex(), |
| rowModel.getMinSelectionIndex()); |
| colModel.addSelectionInterval(colModel.getMinSelectionIndex(), |
| colModel.getMinSelectionIndex()); |
| return; |
| } |
| |
| // multRowsSelected and multColsSelected tell us if multiple rows or |
| // columns are selected, respectively |
| boolean multRowsSelected, multColsSelected; |
| multRowsSelected = table.getSelectedRowCount() > 1 && |
| table.getRowSelectionAllowed(); |
| |
| multColsSelected = table.getSelectedColumnCount() > 1 && |
| table.getColumnSelectionAllowed(); |
| |
| // If there is just one selection, select the next cell, and wrap |
| // when you get to the edges of the table. |
| if (!multColsSelected && !multRowsSelected) |
| { |
| if (command.indexOf("Column") != -1) |
| advanceSingleSelection(colModel, colMax, rowModel, rowMax, |
| command.equals("selectPreviousColumnCell")); |
| else |
| advanceSingleSelection(rowModel, rowMax, colModel, colMax, |
| command.equals("selectPreviousRowCell")); |
| return; |
| } |
| |
| |
| // rowMinSelected and rowMaxSelected are the minimum and maximum |
| // values respectively of selected cells in the row selection model |
| // Similarly for colMinSelected and colMaxSelected. |
| int rowMaxSelected = table.getRowSelectionAllowed() ? |
| rowModel.getMaxSelectionIndex() : table.getModel().getRowCount() - 1; |
| int rowMinSelected = table.getRowSelectionAllowed() ? |
| rowModel.getMinSelectionIndex() : 0; |
| int colMaxSelected = table.getColumnSelectionAllowed() ? |
| colModel.getMaxSelectionIndex() : |
| table.getModel().getColumnCount() - 1; |
| int colMinSelected = table.getColumnSelectionAllowed() ? |
| colModel.getMinSelectionIndex() : 0; |
| |
| // If there are multiple rows and columns selected, select the next |
| // cell and wrap at the edges of the selection. |
| if (command.indexOf("Column") != -1) |
| advanceMultipleSelection(table, colModel, colMinSelected, |
| colMaxSelected, rowModel, rowMinSelected, |
| rowMaxSelected, |
| command.equals("selectPreviousColumnCell"), |
| true); |
| |
| else |
| advanceMultipleSelection(table, rowModel, rowMinSelected, |
| rowMaxSelected, colModel, colMinSelected, |
| colMaxSelected, |
| command.equals("selectPreviousRowCell"), |
| false); |
| } |
| else if (command.equals("selectNextColumn")) |
| { |
| colModel.setSelectionInterval(Math.min(colLead + 1, colMax), |
| Math.min(colLead + 1, colMax)); |
| } |
| else if (command.equals("scrollLeftExtendSelection")) |
| { |
| int target; |
| if (colLead == getFirstVisibleColumnIndex(table)) |
| target = Math.max(0, colLead - (getLastVisibleColumnIndex(table) |
| - getFirstVisibleColumnIndex(table) + 1)); |
| else |
| target = getFirstVisibleColumnIndex(table); |
| |
| colModel.setLeadSelectionIndex(target); |
| rowModel.setLeadSelectionIndex(rowLead); |
| } |
| else if (command.equals("scrollDownChangeSelection")) |
| { |
| int target; |
| if (rowLead == getLastVisibleRowIndex(table)) |
| target = Math.min(rowMax, rowLead + (getLastVisibleRowIndex(table) |
| - getFirstVisibleRowIndex(table) + 1)); |
| else |
| target = getLastVisibleRowIndex(table); |
| |
| rowModel.setSelectionInterval(target, target); |
| colModel.setSelectionInterval(colLead, colLead); |
| } |
| else if (command.equals("scrollRightExtendSelection")) |
| { |
| int target; |
| if (colLead == getLastVisibleColumnIndex(table)) |
| target = Math.min(colMax, colLead + (getLastVisibleColumnIndex(table) |
| - getFirstVisibleColumnIndex(table) + 1)); |
| else |
| target = getLastVisibleColumnIndex(table); |
| |
| colModel.setLeadSelectionIndex(target); |
| rowModel.setLeadSelectionIndex(rowLead); |
| } |
| else if (command.equals("selectAll")) |
| { |
| table.selectAll(); |
| } |
| else if (command.equals("selectLastRowExtendSelection")) |
| { |
| rowModel.setLeadSelectionIndex(rowMax); |
| colModel.setLeadSelectionIndex(colLead); |
| } |
| else if (command.equals("scrollDownExtendSelection")) |
| { |
| int target; |
| if (rowLead == getLastVisibleRowIndex(table)) |
| target = Math.min(rowMax, rowLead + (getLastVisibleRowIndex(table) |
| - getFirstVisibleRowIndex(table) + 1)); |
| else |
| target = getLastVisibleRowIndex(table); |
| |
| rowModel.setLeadSelectionIndex(target); |
| colModel.setLeadSelectionIndex(colLead); |
| } |
| else if (command.equals("scrollUpChangeSelection")) |
| { |
| int target; |
| if (rowLead == getFirstVisibleRowIndex(table)) |
| target = Math.max(0, rowLead - (getLastVisibleRowIndex(table) |
| - getFirstVisibleRowIndex(table) + 1)); |
| else |
| target = getFirstVisibleRowIndex(table); |
| |
| rowModel.setSelectionInterval(target, target); |
| colModel.setSelectionInterval(colLead, colLead); |
| } |
| else if (command.equals("selectNextRowChangeLead")) |
| { |
| if (rowModel.getSelectionMode() |
| != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) |
| { |
| // just "selectNextRow" |
| rowModel.setSelectionInterval(Math.min(rowLead + 1, rowMax), |
| Math.min(rowLead + 1, rowMax)); |
| colModel.setSelectionInterval(colLead, colLead); |
| } |
| else |
| rowModel.moveLeadSelectionIndex(Math.min(rowLead + 1, rowMax)); |
| } |
| else if (command.equals("selectPreviousRowChangeLead")) |
| { |
| if (rowModel.getSelectionMode() |
| != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) |
| { |
| // just selectPreviousRow |
| rowModel.setSelectionInterval(Math.max(rowLead - 1, 0), |
| Math.min(rowLead - 1, 0)); |
| colModel.setSelectionInterval(colLead, colLead); |
| } |
| else |
| rowModel.moveLeadSelectionIndex(Math.max(rowLead - 1, 0)); |
| } |
| else if (command.equals("selectNextColumnChangeLead")) |
| { |
| if (colModel.getSelectionMode() |
| != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) |
| { |
| // just selectNextColumn |
| rowModel.setSelectionInterval(rowLead, rowLead); |
| colModel.setSelectionInterval(Math.min(colLead + 1, colMax), |
| Math.min(colLead + 1, colMax)); |
| } |
| else |
| colModel.moveLeadSelectionIndex(Math.min(colLead + 1, colMax)); |
| } |
| else if (command.equals("selectPreviousColumnChangeLead")) |
| { |
| if (colModel.getSelectionMode() |
| != ListSelectionModel.MULTIPLE_INTERVAL_SELECTION) |
| { |
| // just selectPreviousColumn |
| rowModel.setSelectionInterval(rowLead, rowLead); |
| colModel.setSelectionInterval(Math.max(colLead - 1, 0), |
| Math.max(colLead - 1, 0)); |
| |
| } |
| else |
| colModel.moveLeadSelectionIndex(Math.max(colLead - 1, 0)); |
| } |
| else if (command.equals("addToSelection")) |
| { |
| if (!table.isEditing()) |
| { |
| int oldRowAnchor = rowModel.getAnchorSelectionIndex(); |
| int oldColAnchor = colModel.getAnchorSelectionIndex(); |
| rowModel.addSelectionInterval(rowLead, rowLead); |
| colModel.addSelectionInterval(colLead, colLead); |
| rowModel.setAnchorSelectionIndex(oldRowAnchor); |
| colModel.setAnchorSelectionIndex(oldColAnchor); |
| } |
| } |
| else if (command.equals("extendTo")) |
| { |
| rowModel.setSelectionInterval(rowModel.getAnchorSelectionIndex(), |
| rowLead); |
| colModel.setSelectionInterval(colModel.getAnchorSelectionIndex(), |
| colLead); |
| } |
| else if (command.equals("toggleAndAnchor")) |
| { |
| if (rowModel.isSelectedIndex(rowLead)) |
| rowModel.removeSelectionInterval(rowLead, rowLead); |
| else |
| rowModel.addSelectionInterval(rowLead, rowLead); |
| |
| if (colModel.isSelectedIndex(colLead)) |
| colModel.removeSelectionInterval(colLead, colLead); |
| else |
| colModel.addSelectionInterval(colLead, colLead); |
| |
| rowModel.setAnchorSelectionIndex(rowLead); |
| colModel.setAnchorSelectionIndex(colLead); |
| } |
| else if (command.equals("stopEditing")) |
| { |
| table.editingStopped(new ChangeEvent(command)); |
| } |
| else |
| { |
| // If we're here that means we bound this TableAction class |
| // to a keyboard input but we either want to ignore that input |
| // or we just haven't implemented its action yet. |
| |
| // Uncomment the following line to print the names of unused bindings |
| // when their keys are pressed |
| |
| // System.out.println ("not implemented: "+e.getActionCommand()); |
| } |
| |
| // Any commands whose keyStrokes should be used by the Editor should not |
| // cause editing to be stopped: ie, the SPACE sends "addToSelection" but |
| // if the table is in editing mode, the space should not cause us to stop |
| // editing because it should be used by the Editor. |
| if (table.isEditing() && command != "startEditing" |
| && command != "addToSelection") |
| table.editingStopped(new ChangeEvent("update")); |
| |
| table.scrollRectToVisible(table.getCellRect( |
| rowModel.getLeadSelectionIndex(), colModel.getLeadSelectionIndex(), |
| false)); |
| } |
| |
| /** |
| * Returns the column index of the first visible column. |
| * @return the column index of the first visible column. |
| */ |
| int getFirstVisibleColumnIndex(JTable table) |
| { |
| ComponentOrientation or = table.getComponentOrientation(); |
| Rectangle r = table.getVisibleRect(); |
| if (!or.isLeftToRight()) |
| r.translate((int) r.getWidth() - 1, 0); |
| return table.columnAtPoint(r.getLocation()); |
| } |
| |
| /** |
| * Returns the column index of the last visible column. |
| * |
| */ |
| int getLastVisibleColumnIndex(JTable table) |
| { |
| ComponentOrientation or = table.getComponentOrientation(); |
| Rectangle r = table.getVisibleRect(); |
| if (or.isLeftToRight()) |
| r.translate((int) r.getWidth() - 1, 0); |
| return table.columnAtPoint(r.getLocation()); |
| } |
| |
| /** |
| * Returns the row index of the first visible row. |
| * |
| */ |
| int getFirstVisibleRowIndex(JTable table) |
| { |
| ComponentOrientation or = table.getComponentOrientation(); |
| Rectangle r = table.getVisibleRect(); |
| if (!or.isLeftToRight()) |
| r.translate((int) r.getWidth() - 1, 0); |
| return table.rowAtPoint(r.getLocation()); |
| } |
| |
| /** |
| * Returns the row index of the last visible row. |
| * |
| */ |
| int getLastVisibleRowIndex(JTable table) |
| { |
| ComponentOrientation or = table.getComponentOrientation(); |
| Rectangle r = table.getVisibleRect(); |
| r.translate(0, (int) r.getHeight() - 1); |
| if (or.isLeftToRight()) |
| r.translate((int) r.getWidth() - 1, 0); |
| // The next if makes sure that we don't return -1 simply because |
| // there is white space at the bottom of the table (ie, the display |
| // area is larger than the table) |
| if (table.rowAtPoint(r.getLocation()) == -1) |
| { |
| if (getFirstVisibleRowIndex(table) == -1) |
| return -1; |
| else |
| return table.getModel().getRowCount() - 1; |
| } |
| return table.rowAtPoint(r.getLocation()); |
| } |
| |
| /** |
| * A helper method for the key bindings. Used because the actions |
| * for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar. |
| * |
| * Selects the next (previous if SHIFT pressed) column for TAB, or row for |
| * ENTER from within the currently selected cells. |
| * |
| * @param firstModel the ListSelectionModel for columns (TAB) or |
| * rows (ENTER) |
| * @param firstMin the first selected index in firstModel |
| * @param firstMax the last selected index in firstModel |
| * @param secondModel the ListSelectionModel for rows (TAB) or |
| * columns (ENTER) |
| * @param secondMin the first selected index in secondModel |
| * @param secondMax the last selected index in secondModel |
| * @param reverse true if shift was held for the event |
| * @param eventIsTab true if TAB was pressed, false if ENTER pressed |
| */ |
| void advanceMultipleSelection(JTable table, ListSelectionModel firstModel, |
| int firstMin, |
| int firstMax, ListSelectionModel secondModel, |
| int secondMin, int secondMax, boolean reverse, |
| boolean eventIsTab) |
| { |
| // If eventIsTab, all the "firsts" correspond to columns, otherwise, to |
| // rows "seconds" correspond to the opposite |
| int firstLead = firstModel.getLeadSelectionIndex(); |
| int secondLead = secondModel.getLeadSelectionIndex(); |
| int numFirsts = eventIsTab ? |
| table.getModel().getColumnCount() : table.getModel().getRowCount(); |
| int numSeconds = eventIsTab ? |
| table.getModel().getRowCount() : table.getModel().getColumnCount(); |
| |
| // check if we have to wrap the "firsts" around, going to the other side |
| if ((firstLead == firstMax && !reverse) || |
| (reverse && firstLead == firstMin)) |
| { |
| firstModel.addSelectionInterval(reverse ? firstMax : firstMin, |
| reverse ? firstMax : firstMin); |
| |
| // check if we have to wrap the "seconds" |
| if ((secondLead == secondMax && !reverse) || |
| (reverse && secondLead == secondMin)) |
| secondModel.addSelectionInterval(reverse ? secondMax : secondMin, |
| reverse ? secondMax : secondMin); |
| |
| // if we're not wrapping the seconds, we have to find out where we |
| // are within the secondModel and advance to the next cell (or |
| // go back to the previous cell if reverse == true) |
| else |
| { |
| int[] secondsSelected; |
| if (eventIsTab && table.getRowSelectionAllowed() || |
| !eventIsTab && table.getColumnSelectionAllowed()) |
| secondsSelected = eventIsTab ? |
| table.getSelectedRows() : table.getSelectedColumns(); |
| else |
| { |
| // if row selection is not allowed, then the entire column gets |
| // selected when you click on it, so consider ALL rows selected |
| secondsSelected = new int[numSeconds]; |
| for (int i = 0; i < numSeconds; i++) |
| secondsSelected[i] = i; |
| } |
| |
| // and now find the "next" index within the model |
| int secondIndex = reverse ? secondsSelected.length - 1 : 0; |
| if (!reverse) |
| while (secondsSelected[secondIndex] <= secondLead) |
| secondIndex++; |
| else |
| while (secondsSelected[secondIndex] >= secondLead) |
| secondIndex--; |
| |
| // and select it - updating the lead selection index |
| secondModel.addSelectionInterval(secondsSelected[secondIndex], |
| secondsSelected[secondIndex]); |
| } |
| } |
| // We didn't have to wrap the firsts, so just find the "next" first |
| // and select it, we don't have to change "seconds" |
| else |
| { |
| int[] firstsSelected; |
| if (eventIsTab && table.getColumnSelectionAllowed() || |
| !eventIsTab && table.getRowSelectionAllowed()) |
| firstsSelected = eventIsTab ? |
| table.getSelectedColumns() : table.getSelectedRows(); |
| else |
| { |
| // if selection not allowed, consider ALL firsts to be selected |
| firstsSelected = new int[numFirsts]; |
| for (int i = 0; i < numFirsts; i++) |
| firstsSelected[i] = i; |
| } |
| int firstIndex = reverse ? firstsSelected.length - 1 : 0; |
| if (!reverse) |
| while (firstsSelected[firstIndex] <= firstLead) |
| firstIndex++; |
| else |
| while (firstsSelected[firstIndex] >= firstLead) |
| firstIndex--; |
| firstModel.addSelectionInterval(firstsSelected[firstIndex], |
| firstsSelected[firstIndex]); |
| secondModel.addSelectionInterval(secondLead, secondLead); |
| } |
| } |
| |
| /** |
| * A helper method for the key bindings. Used because the actions |
| * for TAB, SHIFT-TAB, ENTER, and SHIFT-ENTER are very similar. |
| * |
| * Selects the next (previous if SHIFT pressed) column (TAB) or row (ENTER) |
| * in the table, changing the current selection. All cells in the table |
| * are eligible, not just the ones that are currently selected. |
| * @param firstModel the ListSelectionModel for columns (TAB) or rows |
| * (ENTER) |
| * @param firstMax the last index in firstModel |
| * @param secondModel the ListSelectionModel for rows (TAB) or columns |
| * (ENTER) |
| * @param secondMax the last index in secondModel |
| * @param reverse true if SHIFT was pressed for the event |
| */ |
| |
| void advanceSingleSelection(ListSelectionModel firstModel, int firstMax, |
| ListSelectionModel secondModel, int secondMax, |
| boolean reverse) |
| { |
| // for TABs, "first" corresponds to columns and "seconds" to rows. |
| // the opposite is true for ENTERs |
| int firstLead = firstModel.getLeadSelectionIndex(); |
| int secondLead = secondModel.getLeadSelectionIndex(); |
| |
| // if we are going backwards subtract 2 because we later add 1 |
| // for a net change of -1 |
| if (reverse && (firstLead == 0)) |
| { |
| // check if we have to wrap around |
| if (secondLead == 0) |
| secondLead += secondMax + 1; |
| secondLead -= 2; |
| } |
| |
| // do we have to wrap the "seconds"? |
| if (reverse && (firstLead == 0) || !reverse && (firstLead == firstMax)) |
| secondModel.setSelectionInterval((secondLead + 1) % (secondMax + 1), |
| (secondLead + 1) % (secondMax + 1)); |
| // if not, just reselect the current lead |
| else |
| secondModel.setSelectionInterval(secondLead, secondLead); |
| |
| // if we are going backwards, subtract 2 because we add 1 later |
| // for net change of -1 |
| if (reverse) |
| { |
| // check for wraparound |
| if (firstLead == 0) |
| firstLead += firstMax + 1; |
| firstLead -= 2; |
| } |
| // select the next "first" |
| firstModel.setSelectionInterval((firstLead + 1) % (firstMax + 1), |
| (firstLead + 1) % (firstMax + 1)); |
| } |
| } |
| |
| protected void installListeners() |
| { |
| if (focusListener == null) |
| focusListener = createFocusListener(); |
| table.addFocusListener(focusListener); |
| if (keyListener == null) |
| keyListener = createKeyListener(); |
| table.addKeyListener(keyListener); |
| if (mouseInputListener == null) |
| mouseInputListener = createMouseInputListener(); |
| table.addMouseListener(mouseInputListener); |
| table.addMouseMotionListener(mouseInputListener); |
| if (propertyChangeListener == null) |
| propertyChangeListener = new PropertyChangeHandler(); |
| table.addPropertyChangeListener(propertyChangeListener); |
| } |
| |
| /** |
| * Uninstalls UI defaults that have been installed by |
| * {@link #installDefaults()}. |
| */ |
| protected void uninstallDefaults() |
| { |
| // Nothing to do here for now. |
| } |
| |
| /** |
| * Uninstalls the keyboard actions that have been installed by |
| * {@link #installKeyboardActions()}. |
| */ |
| protected void uninstallKeyboardActions() |
| { |
| SwingUtilities.replaceUIInputMap(table, JComponent. |
| WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, null); |
| SwingUtilities.replaceUIActionMap(table, null); |
| } |
| |
| protected void uninstallListeners() |
| { |
| table.removeFocusListener(focusListener); |
| table.removeKeyListener(keyListener); |
| table.removeMouseListener(mouseInputListener); |
| table.removeMouseMotionListener(mouseInputListener); |
| table.removePropertyChangeListener(propertyChangeListener); |
| propertyChangeListener = null; |
| } |
| |
| public void installUI(JComponent comp) |
| { |
| table = (JTable) comp; |
| rendererPane = new CellRendererPane(); |
| table.add(rendererPane); |
| |
| installDefaults(); |
| installKeyboardActions(); |
| installListeners(); |
| } |
| |
| public void uninstallUI(JComponent c) |
| { |
| uninstallListeners(); |
| uninstallKeyboardActions(); |
| uninstallDefaults(); |
| |
| table.remove(rendererPane); |
| rendererPane = null; |
| table = null; |
| } |
| |
| /** |
| * Paints a single cell in the table. |
| * |
| * @param g The graphics context to paint in |
| * @param row The row number to paint |
| * @param col The column number to paint |
| * @param bounds The bounds of the cell to paint, assuming a coordinate |
| * system beginning at <code>(0,0)</code> in the upper left corner of the |
| * table |
| * @param rend A cell renderer to paint with |
| */ |
| void paintCell(Graphics g, int row, int col, Rectangle bounds, |
| TableCellRenderer rend) |
| { |
| Component comp = table.prepareRenderer(rend, row, col); |
| rendererPane.paintComponent(g, comp, table, bounds); |
| } |
| |
| /** |
| * Paint the associated table. |
| */ |
| public void paint(Graphics gfx, JComponent ignored) |
| { |
| int ncols = table.getColumnCount(); |
| int nrows = table.getRowCount(); |
| if (nrows == 0 || ncols == 0) |
| return; |
| |
| Rectangle clip = gfx.getClipBounds(); |
| |
| // Determine the range of cells that are within the clip bounds. |
| Point p1 = new Point(clip.x, clip.y); |
| int c0 = table.columnAtPoint(p1); |
| if (c0 == -1) |
| c0 = 0; |
| int r0 = table.rowAtPoint(p1); |
| if (r0 == -1) |
| r0 = 0; |
| Point p2 = new Point(clip.x + clip.width, clip.y + clip.height); |
| int cn = table.columnAtPoint(p2); |
| if (cn == -1) |
| cn = table.getColumnCount() - 1; |
| int rn = table.rowAtPoint(p2); |
| if (rn == -1) |
| rn = table.getRowCount() - 1; |
| |
| int columnMargin = table.getColumnModel().getColumnMargin(); |
| int rowMargin = table.getRowMargin(); |
| |
| TableColumnModel cmodel = table.getColumnModel(); |
| int[] widths = new int[cn + 1]; |
| for (int i = c0; i <= cn; i++) |
| { |
| widths[i] = cmodel.getColumn(i).getWidth() - columnMargin; |
| } |
| |
| Rectangle bounds = table.getCellRect(r0, c0, false); |
| // The left boundary of the area being repainted. |
| int left = bounds.x; |
| |
| // The top boundary of the area being repainted. |
| int top = bounds.y; |
| |
| // The bottom boundary of the area being repainted. |
| int bottom; |
| |
| // paint the cell contents |
| Color grid = table.getGridColor(); |
| for (int r = r0; r <= rn; ++r) |
| { |
| for (int c = c0; c <= cn; ++c) |
| { |
| bounds.width = widths[c]; |
| paintCell(gfx, r, c, bounds, table.getCellRenderer(r, c)); |
| bounds.x += widths[c] + columnMargin; |
| } |
| bounds.x = left; |
| bounds.y += table.getRowHeight(r); |
| // Update row height for tables with custom heights. |
| bounds.height = table.getRowHeight(r + 1) - rowMargin; |
| } |
| |
| bottom = bounds.y - rowMargin; |
| |
| // paint vertical grid lines |
| if (grid != null && table.getShowVerticalLines()) |
| { |
| Color save = gfx.getColor(); |
| gfx.setColor(grid); |
| int x = left - columnMargin; |
| for (int c = c0; c <= cn; ++c) |
| { |
| // The vertical grid is draw right from the cells, so we |
| // add before drawing. |
| x += widths[c] + columnMargin; |
| gfx.drawLine(x, top, x, bottom); |
| } |
| gfx.setColor(save); |
| } |
| |
| // paint horizontal grid lines |
| if (grid != null && table.getShowHorizontalLines()) |
| { |
| Color save = gfx.getColor(); |
| gfx.setColor(grid); |
| int y = top - rowMargin; |
| for (int r = r0; r <= rn; ++r) |
| { |
| // The horizontal grid is draw below the cells, so we |
| // add before drawing. |
| y += table.getRowHeight(r); |
| gfx.drawLine(left, y, p2.x, y); |
| } |
| gfx.setColor(save); |
| } |
| } |
| } |