mirror of
https://github.com/autc04/Retro68.git
synced 2025-01-18 20:31:19 +00:00
1411 lines
50 KiB
Java
1411 lines
50 KiB
Java
/* 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;
|
|
TableColumnModel tcm = table.getColumnModel();
|
|
|
|
for (int i = 0; i < tcm.getColumnCount(); i++)
|
|
{
|
|
TableColumn col = tcm.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);
|
|
}
|
|
}
|
|
}
|