You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
962 lines
33 KiB
962 lines
33 KiB
/*
|
|
* Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
|
|
* ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*
|
|
*/
|
|
|
|
package javax.swing.plaf.basic;
|
|
|
|
import java.awt.*;
|
|
import java.awt.event.*;
|
|
import java.util.*;
|
|
import javax.swing.*;
|
|
import javax.swing.event.*;
|
|
import javax.swing.plaf.*;
|
|
import javax.swing.table.*;
|
|
|
|
import sun.swing.*;
|
|
|
|
/**
|
|
* BasicTableHeaderUI implementation
|
|
*
|
|
* @author Alan Chung
|
|
* @author Philip Milne
|
|
*/
|
|
public class BasicTableHeaderUI extends TableHeaderUI {
|
|
|
|
private static Cursor resizeCursor = Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR);
|
|
|
|
//
|
|
// Instance Variables
|
|
//
|
|
|
|
/** The JTableHeader that is delegating the painting to this UI. */
|
|
protected JTableHeader header;
|
|
protected CellRendererPane rendererPane;
|
|
|
|
// Listeners that are attached to the JTable
|
|
protected MouseInputListener mouseInputListener;
|
|
|
|
// The column header over which the mouse currently is.
|
|
private int rolloverColumn = -1;
|
|
|
|
// The column that should be highlighted when the table header has the focus.
|
|
private int selectedColumnIndex = 0; // Read ONLY via getSelectedColumnIndex!
|
|
|
|
private static FocusListener focusListener = new FocusListener() {
|
|
public void focusGained(FocusEvent e) {
|
|
repaintHeader(e.getSource());
|
|
}
|
|
|
|
public void focusLost(FocusEvent e) {
|
|
repaintHeader(e.getSource());
|
|
}
|
|
|
|
private void repaintHeader(Object source) {
|
|
if (source instanceof JTableHeader) {
|
|
JTableHeader th = (JTableHeader)source;
|
|
BasicTableHeaderUI ui =
|
|
(BasicTableHeaderUI)BasicLookAndFeel.
|
|
getUIOfType(th.getUI(),
|
|
BasicTableHeaderUI.class);
|
|
if (ui == null) {
|
|
return;
|
|
}
|
|
|
|
th.repaint(th.getHeaderRect(ui.getSelectedColumnIndex()));
|
|
}
|
|
}
|
|
};
|
|
|
|
/**
|
|
* This class should be treated as a "protected" inner class.
|
|
* Instantiate it only within subclasses of {@code BasicTableHeaderUI}.
|
|
*/
|
|
public class MouseInputHandler implements MouseInputListener {
|
|
|
|
private int mouseXOffset;
|
|
private Cursor otherCursor = resizeCursor;
|
|
|
|
public void mouseClicked(MouseEvent e) {
|
|
if (!header.isEnabled()) {
|
|
return;
|
|
}
|
|
if (e.getClickCount() % 2 == 1 &&
|
|
SwingUtilities.isLeftMouseButton(e)) {
|
|
JTable table = header.getTable();
|
|
RowSorter sorter;
|
|
if (table != null && (sorter = table.getRowSorter()) != null) {
|
|
int columnIndex = header.columnAtPoint(e.getPoint());
|
|
if (columnIndex != -1) {
|
|
columnIndex = table.convertColumnIndexToModel(
|
|
columnIndex);
|
|
sorter.toggleSortOrder(columnIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private TableColumn getResizingColumn(Point p) {
|
|
return getResizingColumn(p, header.columnAtPoint(p));
|
|
}
|
|
|
|
private TableColumn getResizingColumn(Point p, int column) {
|
|
if (column == -1) {
|
|
return null;
|
|
}
|
|
Rectangle r = header.getHeaderRect(column);
|
|
r.grow(-3, 0);
|
|
if (r.contains(p)) {
|
|
return null;
|
|
}
|
|
int midPoint = r.x + r.width/2;
|
|
int columnIndex;
|
|
if( header.getComponentOrientation().isLeftToRight() ) {
|
|
columnIndex = (p.x < midPoint) ? column - 1 : column;
|
|
} else {
|
|
columnIndex = (p.x < midPoint) ? column : column - 1;
|
|
}
|
|
if (columnIndex == -1) {
|
|
return null;
|
|
}
|
|
return header.getColumnModel().getColumn(columnIndex);
|
|
}
|
|
|
|
public void mousePressed(MouseEvent e) {
|
|
if (!header.isEnabled()) {
|
|
return;
|
|
}
|
|
header.setDraggedColumn(null);
|
|
header.setResizingColumn(null);
|
|
header.setDraggedDistance(0);
|
|
|
|
Point p = e.getPoint();
|
|
|
|
// First find which header cell was hit
|
|
TableColumnModel columnModel = header.getColumnModel();
|
|
int index = header.columnAtPoint(p);
|
|
|
|
if (index != -1) {
|
|
// The last 3 pixels + 3 pixels of next column are for resizing
|
|
TableColumn resizingColumn = getResizingColumn(p, index);
|
|
if (canResize(resizingColumn, header)) {
|
|
header.setResizingColumn(resizingColumn);
|
|
if( header.getComponentOrientation().isLeftToRight() ) {
|
|
mouseXOffset = p.x - resizingColumn.getWidth();
|
|
} else {
|
|
mouseXOffset = p.x + resizingColumn.getWidth();
|
|
}
|
|
}
|
|
else if (header.getReorderingAllowed()) {
|
|
TableColumn hitColumn = columnModel.getColumn(index);
|
|
header.setDraggedColumn(hitColumn);
|
|
mouseXOffset = p.x;
|
|
}
|
|
}
|
|
|
|
if (header.getReorderingAllowed()) {
|
|
int oldRolloverColumn = rolloverColumn;
|
|
rolloverColumn = -1;
|
|
rolloverColumnUpdated(oldRolloverColumn, rolloverColumn);
|
|
}
|
|
}
|
|
|
|
private void swapCursor() {
|
|
Cursor tmp = header.getCursor();
|
|
header.setCursor(otherCursor);
|
|
otherCursor = tmp;
|
|
}
|
|
|
|
public void mouseMoved(MouseEvent e) {
|
|
if (!header.isEnabled()) {
|
|
return;
|
|
}
|
|
if (canResize(getResizingColumn(e.getPoint()), header) !=
|
|
(header.getCursor() == resizeCursor)) {
|
|
swapCursor();
|
|
}
|
|
updateRolloverColumn(e);
|
|
}
|
|
|
|
public void mouseDragged(MouseEvent e) {
|
|
if (!header.isEnabled()) {
|
|
return;
|
|
}
|
|
int mouseX = e.getX();
|
|
|
|
TableColumn resizingColumn = header.getResizingColumn();
|
|
TableColumn draggedColumn = header.getDraggedColumn();
|
|
|
|
boolean headerLeftToRight = header.getComponentOrientation().isLeftToRight();
|
|
|
|
if (resizingColumn != null) {
|
|
int oldWidth = resizingColumn.getWidth();
|
|
int newWidth;
|
|
if (headerLeftToRight) {
|
|
newWidth = mouseX - mouseXOffset;
|
|
} else {
|
|
newWidth = mouseXOffset - mouseX;
|
|
}
|
|
mouseXOffset += changeColumnWidth(resizingColumn, header,
|
|
oldWidth, newWidth);
|
|
}
|
|
else if (draggedColumn != null) {
|
|
TableColumnModel cm = header.getColumnModel();
|
|
int draggedDistance = mouseX - mouseXOffset;
|
|
int direction = (draggedDistance < 0) ? -1 : 1;
|
|
int columnIndex = viewIndexForColumn(draggedColumn);
|
|
int newColumnIndex = columnIndex + (headerLeftToRight ? direction : -direction);
|
|
if (0 <= newColumnIndex && newColumnIndex < cm.getColumnCount()) {
|
|
int width = cm.getColumn(newColumnIndex).getWidth();
|
|
if (Math.abs(draggedDistance) > (width / 2)) {
|
|
|
|
mouseXOffset = mouseXOffset + direction * width;
|
|
header.setDraggedDistance(draggedDistance - direction * width);
|
|
|
|
//Cache the selected column.
|
|
int selectedIndex =
|
|
SwingUtilities2.convertColumnIndexToModel(
|
|
header.getColumnModel(),
|
|
getSelectedColumnIndex());
|
|
|
|
//Now do the move.
|
|
cm.moveColumn(columnIndex, newColumnIndex);
|
|
|
|
//Update the selected index.
|
|
selectColumn(
|
|
SwingUtilities2.convertColumnIndexToView(
|
|
header.getColumnModel(), selectedIndex),
|
|
false);
|
|
|
|
return;
|
|
}
|
|
}
|
|
setDraggedDistance(draggedDistance, columnIndex);
|
|
}
|
|
|
|
updateRolloverColumn(e);
|
|
}
|
|
|
|
public void mouseReleased(MouseEvent e) {
|
|
if (!header.isEnabled()) {
|
|
return;
|
|
}
|
|
setDraggedDistance(0, viewIndexForColumn(header.getDraggedColumn()));
|
|
|
|
header.setResizingColumn(null);
|
|
header.setDraggedColumn(null);
|
|
|
|
updateRolloverColumn(e);
|
|
}
|
|
|
|
public void mouseEntered(MouseEvent e) {
|
|
if (!header.isEnabled()) {
|
|
return;
|
|
}
|
|
updateRolloverColumn(e);
|
|
}
|
|
|
|
public void mouseExited(MouseEvent e) {
|
|
if (!header.isEnabled()) {
|
|
return;
|
|
}
|
|
int oldRolloverColumn = rolloverColumn;
|
|
rolloverColumn = -1;
|
|
rolloverColumnUpdated(oldRolloverColumn, rolloverColumn);
|
|
}
|
|
//
|
|
// Protected & Private Methods
|
|
//
|
|
|
|
private void setDraggedDistance(int draggedDistance, int column) {
|
|
header.setDraggedDistance(draggedDistance);
|
|
if (column != -1) {
|
|
header.getColumnModel().moveColumn(column, column);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Factory methods for the Listeners
|
|
//
|
|
|
|
/**
|
|
* Creates the mouse listener for the JTableHeader.
|
|
*/
|
|
protected MouseInputListener createMouseInputListener() {
|
|
return new MouseInputHandler();
|
|
}
|
|
|
|
//
|
|
// The installation/uninstall procedures and support
|
|
//
|
|
|
|
public static ComponentUI createUI(JComponent h) {
|
|
return new BasicTableHeaderUI();
|
|
}
|
|
|
|
// Installation
|
|
|
|
public void installUI(JComponent c) {
|
|
header = (JTableHeader)c;
|
|
|
|
rendererPane = new CellRendererPane();
|
|
header.add(rendererPane);
|
|
|
|
installDefaults();
|
|
installListeners();
|
|
installKeyboardActions();
|
|
}
|
|
|
|
/**
|
|
* Initializes JTableHeader properties such as font, foreground, and background.
|
|
* The font, foreground, and background properties are only set if their
|
|
* current value is either null or a UIResource, other properties are set
|
|
* if the current value is null.
|
|
*
|
|
* @see #installUI
|
|
*/
|
|
protected void installDefaults() {
|
|
LookAndFeel.installColorsAndFont(header, "TableHeader.background",
|
|
"TableHeader.foreground", "TableHeader.font");
|
|
LookAndFeel.installProperty(header, "opaque", Boolean.TRUE);
|
|
}
|
|
|
|
/**
|
|
* Attaches listeners to the JTableHeader.
|
|
*/
|
|
protected void installListeners() {
|
|
mouseInputListener = createMouseInputListener();
|
|
|
|
header.addMouseListener(mouseInputListener);
|
|
header.addMouseMotionListener(mouseInputListener);
|
|
header.addFocusListener(focusListener);
|
|
}
|
|
|
|
/**
|
|
* Register all keyboard actions on the JTableHeader.
|
|
*/
|
|
protected void installKeyboardActions() {
|
|
InputMap keyMap = (InputMap)DefaultLookup.get(header, this,
|
|
"TableHeader.ancestorInputMap");
|
|
SwingUtilities.replaceUIInputMap(header,
|
|
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, keyMap);
|
|
LazyActionMap.installLazyActionMap(header, BasicTableHeaderUI.class,
|
|
"TableHeader.actionMap");
|
|
}
|
|
|
|
// Uninstall methods
|
|
|
|
public void uninstallUI(JComponent c) {
|
|
uninstallDefaults();
|
|
uninstallListeners();
|
|
uninstallKeyboardActions();
|
|
|
|
header.remove(rendererPane);
|
|
rendererPane = null;
|
|
header = null;
|
|
}
|
|
|
|
protected void uninstallDefaults() {}
|
|
|
|
protected void uninstallListeners() {
|
|
header.removeMouseListener(mouseInputListener);
|
|
header.removeMouseMotionListener(mouseInputListener);
|
|
|
|
mouseInputListener = null;
|
|
}
|
|
|
|
/**
|
|
* Unregisters default key actions.
|
|
*/
|
|
protected void uninstallKeyboardActions() {
|
|
SwingUtilities.replaceUIInputMap(header, JComponent.WHEN_FOCUSED, null);
|
|
SwingUtilities.replaceUIActionMap(header, null);
|
|
}
|
|
|
|
/**
|
|
* Populates TableHeader's actions.
|
|
*/
|
|
static void loadActionMap(LazyActionMap map) {
|
|
map.put(new Actions(Actions.TOGGLE_SORT_ORDER));
|
|
map.put(new Actions(Actions.SELECT_COLUMN_TO_LEFT));
|
|
map.put(new Actions(Actions.SELECT_COLUMN_TO_RIGHT));
|
|
map.put(new Actions(Actions.MOVE_COLUMN_LEFT));
|
|
map.put(new Actions(Actions.MOVE_COLUMN_RIGHT));
|
|
map.put(new Actions(Actions.RESIZE_LEFT));
|
|
map.put(new Actions(Actions.RESIZE_RIGHT));
|
|
map.put(new Actions(Actions.FOCUS_TABLE));
|
|
}
|
|
|
|
//
|
|
// Support for mouse rollover
|
|
//
|
|
|
|
/**
|
|
* Returns the index of the column header over which the mouse
|
|
* currently is. When the mouse is not over the table header,
|
|
* -1 is returned.
|
|
*
|
|
* @see #rolloverColumnUpdated(int, int)
|
|
* @return the index of the current rollover column
|
|
* @since 1.6
|
|
*/
|
|
protected int getRolloverColumn() {
|
|
return rolloverColumn;
|
|
}
|
|
|
|
/**
|
|
* This method gets called every time when a rollover column in the table
|
|
* header is updated. Every look and feel that supports a rollover effect
|
|
* in a table header should override this method and repaint the header.
|
|
*
|
|
* @param oldColumn the index of the previous rollover column or -1 if the
|
|
* mouse was not over a column
|
|
* @param newColumn the index of the new rollover column or -1 if the mouse
|
|
* is not over a column
|
|
* @see #getRolloverColumn()
|
|
* @see JTableHeader#getHeaderRect(int)
|
|
* @since 1.6
|
|
*/
|
|
protected void rolloverColumnUpdated(int oldColumn, int newColumn) {
|
|
}
|
|
|
|
private void updateRolloverColumn(MouseEvent e) {
|
|
if (header.getDraggedColumn() == null &&
|
|
header.contains(e.getPoint())) {
|
|
|
|
int col = header.columnAtPoint(e.getPoint());
|
|
if (col != rolloverColumn) {
|
|
int oldRolloverColumn = rolloverColumn;
|
|
rolloverColumn = col;
|
|
rolloverColumnUpdated(oldRolloverColumn, rolloverColumn);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Support for keyboard and mouse access
|
|
//
|
|
private int selectNextColumn(boolean doIt) {
|
|
int newIndex = getSelectedColumnIndex();
|
|
if (newIndex < header.getColumnModel().getColumnCount() - 1) {
|
|
newIndex++;
|
|
if (doIt) {
|
|
selectColumn(newIndex);
|
|
}
|
|
}
|
|
return newIndex;
|
|
}
|
|
|
|
private int selectPreviousColumn(boolean doIt) {
|
|
int newIndex = getSelectedColumnIndex();
|
|
if (newIndex > 0) {
|
|
newIndex--;
|
|
if (doIt) {
|
|
selectColumn(newIndex);
|
|
}
|
|
}
|
|
return newIndex;
|
|
}
|
|
|
|
/**
|
|
* Selects the specified column in the table header. Repaints the
|
|
* affected header cells and makes sure the newly selected one is visible.
|
|
*/
|
|
void selectColumn(int newColIndex) {
|
|
selectColumn(newColIndex, true);
|
|
}
|
|
|
|
void selectColumn(int newColIndex, boolean doScroll) {
|
|
Rectangle repaintRect = header.getHeaderRect(selectedColumnIndex);
|
|
header.repaint(repaintRect);
|
|
selectedColumnIndex = newColIndex;
|
|
repaintRect = header.getHeaderRect(newColIndex);
|
|
header.repaint(repaintRect);
|
|
if (doScroll) {
|
|
scrollToColumn(newColIndex);
|
|
}
|
|
return;
|
|
}
|
|
/**
|
|
* Used by selectColumn to scroll horizontally, if necessary,
|
|
* to ensure that the newly selected column is visible.
|
|
*/
|
|
private void scrollToColumn(int col) {
|
|
Container container;
|
|
JTable table;
|
|
|
|
//Test whether the header is in a scroll pane and has a table.
|
|
if ((header.getParent() == null) ||
|
|
((container = header.getParent().getParent()) == null) ||
|
|
!(container instanceof JScrollPane) ||
|
|
((table = header.getTable()) == null)) {
|
|
return;
|
|
}
|
|
|
|
//Now scroll, if necessary.
|
|
Rectangle vis = table.getVisibleRect();
|
|
Rectangle cellBounds = table.getCellRect(0, col, true);
|
|
vis.x = cellBounds.x;
|
|
vis.width = cellBounds.width;
|
|
table.scrollRectToVisible(vis);
|
|
}
|
|
|
|
private int getSelectedColumnIndex() {
|
|
int numCols = header.getColumnModel().getColumnCount();
|
|
if (selectedColumnIndex >= numCols && numCols > 0) {
|
|
selectedColumnIndex = numCols - 1;
|
|
}
|
|
return selectedColumnIndex;
|
|
}
|
|
|
|
private static boolean canResize(TableColumn column,
|
|
JTableHeader header) {
|
|
return (column != null) && header.getResizingAllowed()
|
|
&& column.getResizable();
|
|
}
|
|
|
|
private int changeColumnWidth(TableColumn resizingColumn,
|
|
JTableHeader th,
|
|
int oldWidth, int newWidth) {
|
|
resizingColumn.setWidth(newWidth);
|
|
|
|
Container container;
|
|
JTable table;
|
|
|
|
if ((th.getParent() == null) ||
|
|
((container = th.getParent().getParent()) == null) ||
|
|
!(container instanceof JScrollPane) ||
|
|
((table = th.getTable()) == null)) {
|
|
return 0;
|
|
}
|
|
|
|
if (!container.getComponentOrientation().isLeftToRight() &&
|
|
!th.getComponentOrientation().isLeftToRight()) {
|
|
JViewport viewport = ((JScrollPane)container).getViewport();
|
|
int viewportWidth = viewport.getWidth();
|
|
int diff = newWidth - oldWidth;
|
|
int newHeaderWidth = table.getWidth() + diff;
|
|
|
|
/* Resize a table */
|
|
Dimension tableSize = table.getSize();
|
|
tableSize.width += diff;
|
|
table.setSize(tableSize);
|
|
|
|
/* If this table is in AUTO_RESIZE_OFF mode and
|
|
* has a horizontal scrollbar, we need to update
|
|
* a view's position.
|
|
*/
|
|
if ((newHeaderWidth >= viewportWidth) &&
|
|
(table.getAutoResizeMode() == JTable.AUTO_RESIZE_OFF)) {
|
|
Point p = viewport.getViewPosition();
|
|
p.x = Math.max(0, Math.min(newHeaderWidth - viewportWidth,
|
|
p.x + diff));
|
|
viewport.setViewPosition(p);
|
|
return diff;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
//
|
|
// Baseline
|
|
//
|
|
|
|
/**
|
|
* Returns the baseline.
|
|
*
|
|
* @throws NullPointerException {@inheritDoc}
|
|
* @throws IllegalArgumentException {@inheritDoc}
|
|
* @see javax.swing.JComponent#getBaseline(int, int)
|
|
* @since 1.6
|
|
*/
|
|
public int getBaseline(JComponent c, int width, int height) {
|
|
super.getBaseline(c, width, height);
|
|
int baseline = -1;
|
|
TableColumnModel columnModel = header.getColumnModel();
|
|
for(int column = 0; column < columnModel.getColumnCount();
|
|
column++) {
|
|
TableColumn aColumn = columnModel.getColumn(column);
|
|
Component comp = getHeaderRenderer(column);
|
|
Dimension pref = comp.getPreferredSize();
|
|
int columnBaseline = comp.getBaseline(pref.width, height);
|
|
if (columnBaseline >= 0) {
|
|
if (baseline == -1) {
|
|
baseline = columnBaseline;
|
|
}
|
|
else if (baseline != columnBaseline) {
|
|
baseline = -1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return baseline;
|
|
}
|
|
|
|
//
|
|
// Paint Methods and support
|
|
//
|
|
|
|
public void paint(Graphics g, JComponent c) {
|
|
if (header.getColumnModel().getColumnCount() <= 0) {
|
|
return;
|
|
}
|
|
boolean ltr = header.getComponentOrientation().isLeftToRight();
|
|
|
|
Rectangle clip = g.getClipBounds();
|
|
Point left = clip.getLocation();
|
|
Point right = new Point( clip.x + clip.width - 1, clip.y );
|
|
TableColumnModel cm = header.getColumnModel();
|
|
int cMin = header.columnAtPoint( ltr ? left : right );
|
|
int cMax = header.columnAtPoint( ltr ? right : left );
|
|
// This should never happen.
|
|
if (cMin == -1) {
|
|
cMin = 0;
|
|
}
|
|
// If the table does not have enough columns to fill the view we'll get -1.
|
|
// Replace this with the index of the last column.
|
|
if (cMax == -1) {
|
|
cMax = cm.getColumnCount()-1;
|
|
}
|
|
|
|
TableColumn draggedColumn = header.getDraggedColumn();
|
|
int columnWidth;
|
|
Rectangle cellRect = header.getHeaderRect(ltr ? cMin : cMax);
|
|
TableColumn aColumn;
|
|
if (ltr) {
|
|
for(int column = cMin; column <= cMax ; column++) {
|
|
aColumn = cm.getColumn(column);
|
|
columnWidth = aColumn.getWidth();
|
|
cellRect.width = columnWidth;
|
|
if (aColumn != draggedColumn) {
|
|
paintCell(g, cellRect, column);
|
|
}
|
|
cellRect.x += columnWidth;
|
|
}
|
|
} else {
|
|
for(int column = cMax; column >= cMin; column--) {
|
|
aColumn = cm.getColumn(column);
|
|
columnWidth = aColumn.getWidth();
|
|
cellRect.width = columnWidth;
|
|
if (aColumn != draggedColumn) {
|
|
paintCell(g, cellRect, column);
|
|
}
|
|
cellRect.x += columnWidth;
|
|
}
|
|
}
|
|
|
|
// Paint the dragged column if we are dragging.
|
|
if (draggedColumn != null) {
|
|
int draggedColumnIndex = viewIndexForColumn(draggedColumn);
|
|
Rectangle draggedCellRect = header.getHeaderRect(draggedColumnIndex);
|
|
|
|
// Draw a gray well in place of the moving column.
|
|
g.setColor(header.getParent().getBackground());
|
|
g.fillRect(draggedCellRect.x, draggedCellRect.y,
|
|
draggedCellRect.width, draggedCellRect.height);
|
|
|
|
draggedCellRect.x += header.getDraggedDistance();
|
|
|
|
// Fill the background.
|
|
g.setColor(header.getBackground());
|
|
g.fillRect(draggedCellRect.x, draggedCellRect.y,
|
|
draggedCellRect.width, draggedCellRect.height);
|
|
|
|
paintCell(g, draggedCellRect, draggedColumnIndex);
|
|
}
|
|
|
|
// Remove all components in the rendererPane.
|
|
rendererPane.removeAll();
|
|
}
|
|
|
|
private Component getHeaderRenderer(int columnIndex) {
|
|
TableColumn aColumn = header.getColumnModel().getColumn(columnIndex);
|
|
TableCellRenderer renderer = aColumn.getHeaderRenderer();
|
|
if (renderer == null) {
|
|
renderer = header.getDefaultRenderer();
|
|
}
|
|
|
|
boolean hasFocus = !header.isPaintingForPrint()
|
|
&& (columnIndex == getSelectedColumnIndex())
|
|
&& header.hasFocus();
|
|
return renderer.getTableCellRendererComponent(header.getTable(),
|
|
aColumn.getHeaderValue(),
|
|
false, hasFocus,
|
|
-1, columnIndex);
|
|
}
|
|
|
|
private void paintCell(Graphics g, Rectangle cellRect, int columnIndex) {
|
|
Component component = getHeaderRenderer(columnIndex);
|
|
rendererPane.paintComponent(g, component, header, cellRect.x, cellRect.y,
|
|
cellRect.width, cellRect.height, true);
|
|
}
|
|
|
|
private int viewIndexForColumn(TableColumn aColumn) {
|
|
TableColumnModel cm = header.getColumnModel();
|
|
for (int column = 0; column < cm.getColumnCount(); column++) {
|
|
if (cm.getColumn(column) == aColumn) {
|
|
return column;
|
|
}
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
//
|
|
// Size Methods
|
|
//
|
|
|
|
private int getHeaderHeight() {
|
|
int height = 0;
|
|
boolean accomodatedDefault = false;
|
|
TableColumnModel columnModel = header.getColumnModel();
|
|
for(int column = 0; column < columnModel.getColumnCount(); column++) {
|
|
TableColumn aColumn = columnModel.getColumn(column);
|
|
boolean isDefault = (aColumn.getHeaderRenderer() == null);
|
|
|
|
if (!isDefault || !accomodatedDefault) {
|
|
Component comp = getHeaderRenderer(column);
|
|
int rendererHeight = comp.getPreferredSize().height;
|
|
height = Math.max(height, rendererHeight);
|
|
|
|
// Configuring the header renderer to calculate its preferred size
|
|
// is expensive. Optimise this by assuming the default renderer
|
|
// always has the same height as the first non-zero height that
|
|
// it returns for a non-null/non-empty value.
|
|
if (isDefault && rendererHeight > 0) {
|
|
Object headerValue = aColumn.getHeaderValue();
|
|
if (headerValue != null) {
|
|
headerValue = headerValue.toString();
|
|
|
|
if (headerValue != null && !headerValue.equals("")) {
|
|
accomodatedDefault = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return height;
|
|
}
|
|
|
|
private Dimension createHeaderSize(long width) {
|
|
// None of the callers include the intercell spacing, do it here.
|
|
if (width > Integer.MAX_VALUE) {
|
|
width = Integer.MAX_VALUE;
|
|
}
|
|
return new Dimension((int)width, getHeaderHeight());
|
|
}
|
|
|
|
|
|
/**
|
|
* Return the minimum size of the header. The minimum width is the sum
|
|
* of the minimum widths of each column (plus inter-cell spacing).
|
|
*/
|
|
public Dimension getMinimumSize(JComponent c) {
|
|
long width = 0;
|
|
Enumeration enumeration = header.getColumnModel().getColumns();
|
|
while (enumeration.hasMoreElements()) {
|
|
TableColumn aColumn = (TableColumn)enumeration.nextElement();
|
|
width = width + aColumn.getMinWidth();
|
|
}
|
|
return createHeaderSize(width);
|
|
}
|
|
|
|
/**
|
|
* Return the preferred size of the header. The preferred height is the
|
|
* maximum of the preferred heights of all of the components provided
|
|
* by the header renderers. The preferred width is the sum of the
|
|
* preferred widths of each column (plus inter-cell spacing).
|
|
*/
|
|
public Dimension getPreferredSize(JComponent c) {
|
|
long width = 0;
|
|
Enumeration enumeration = header.getColumnModel().getColumns();
|
|
while (enumeration.hasMoreElements()) {
|
|
TableColumn aColumn = (TableColumn)enumeration.nextElement();
|
|
width = width + aColumn.getPreferredWidth();
|
|
}
|
|
return createHeaderSize(width);
|
|
}
|
|
|
|
/**
|
|
* Return the maximum size of the header. The maximum width is the sum
|
|
* of the maximum widths of each column (plus inter-cell spacing).
|
|
*/
|
|
public Dimension getMaximumSize(JComponent c) {
|
|
long width = 0;
|
|
Enumeration enumeration = header.getColumnModel().getColumns();
|
|
while (enumeration.hasMoreElements()) {
|
|
TableColumn aColumn = (TableColumn)enumeration.nextElement();
|
|
width = width + aColumn.getMaxWidth();
|
|
}
|
|
return createHeaderSize(width);
|
|
}
|
|
|
|
private static class Actions extends UIAction {
|
|
public static final String TOGGLE_SORT_ORDER =
|
|
"toggleSortOrder";
|
|
public static final String SELECT_COLUMN_TO_LEFT =
|
|
"selectColumnToLeft";
|
|
public static final String SELECT_COLUMN_TO_RIGHT =
|
|
"selectColumnToRight";
|
|
public static final String MOVE_COLUMN_LEFT =
|
|
"moveColumnLeft";
|
|
public static final String MOVE_COLUMN_RIGHT =
|
|
"moveColumnRight";
|
|
public static final String RESIZE_LEFT =
|
|
"resizeLeft";
|
|
public static final String RESIZE_RIGHT =
|
|
"resizeRight";
|
|
public static final String FOCUS_TABLE =
|
|
"focusTable";
|
|
|
|
public Actions(String name) {
|
|
super(name);
|
|
}
|
|
|
|
public boolean isEnabled(Object sender) {
|
|
if (sender instanceof JTableHeader) {
|
|
JTableHeader th = (JTableHeader)sender;
|
|
TableColumnModel cm = th.getColumnModel();
|
|
if (cm.getColumnCount() <= 0) {
|
|
return false;
|
|
}
|
|
|
|
String key = getName();
|
|
BasicTableHeaderUI ui =
|
|
(BasicTableHeaderUI)BasicLookAndFeel.getUIOfType(th.getUI(),
|
|
BasicTableHeaderUI.class);
|
|
if (ui != null) {
|
|
if (key == MOVE_COLUMN_LEFT) {
|
|
return th.getReorderingAllowed()
|
|
&& maybeMoveColumn(true, th, ui, false);
|
|
} else if (key == MOVE_COLUMN_RIGHT) {
|
|
return th.getReorderingAllowed()
|
|
&& maybeMoveColumn(false, th, ui, false);
|
|
} else if (key == RESIZE_LEFT ||
|
|
key == RESIZE_RIGHT) {
|
|
return canResize(cm.getColumn(ui.getSelectedColumnIndex()), th);
|
|
} else if (key == FOCUS_TABLE) {
|
|
return (th.getTable() != null);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public void actionPerformed(ActionEvent e) {
|
|
JTableHeader th = (JTableHeader)e.getSource();
|
|
BasicTableHeaderUI ui =
|
|
(BasicTableHeaderUI)BasicLookAndFeel.
|
|
getUIOfType(th.getUI(),
|
|
BasicTableHeaderUI.class);
|
|
if (ui == null) {
|
|
return;
|
|
}
|
|
|
|
String name = getName();
|
|
if (TOGGLE_SORT_ORDER == name) {
|
|
JTable table = th.getTable();
|
|
RowSorter sorter = table == null ? null : table.getRowSorter();
|
|
if (sorter != null) {
|
|
int columnIndex = ui.getSelectedColumnIndex();
|
|
columnIndex = table.convertColumnIndexToModel(
|
|
columnIndex);
|
|
sorter.toggleSortOrder(columnIndex);
|
|
}
|
|
} else if (SELECT_COLUMN_TO_LEFT == name) {
|
|
if (th.getComponentOrientation().isLeftToRight()) {
|
|
ui.selectPreviousColumn(true);
|
|
} else {
|
|
ui.selectNextColumn(true);
|
|
}
|
|
} else if (SELECT_COLUMN_TO_RIGHT == name) {
|
|
if (th.getComponentOrientation().isLeftToRight()) {
|
|
ui.selectNextColumn(true);
|
|
} else {
|
|
ui.selectPreviousColumn(true);
|
|
}
|
|
} else if (MOVE_COLUMN_LEFT == name) {
|
|
moveColumn(true, th, ui);
|
|
} else if (MOVE_COLUMN_RIGHT == name) {
|
|
moveColumn(false, th, ui);
|
|
} else if (RESIZE_LEFT == name) {
|
|
resize(true, th, ui);
|
|
} else if (RESIZE_RIGHT == name) {
|
|
resize(false, th, ui);
|
|
} else if (FOCUS_TABLE == name) {
|
|
JTable table = th.getTable();
|
|
if (table != null) {
|
|
table.requestFocusInWindow();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void moveColumn(boolean leftArrow, JTableHeader th,
|
|
BasicTableHeaderUI ui) {
|
|
maybeMoveColumn(leftArrow, th, ui, true);
|
|
}
|
|
|
|
private boolean maybeMoveColumn(boolean leftArrow, JTableHeader th,
|
|
BasicTableHeaderUI ui, boolean doIt) {
|
|
int oldIndex = ui.getSelectedColumnIndex();
|
|
int newIndex;
|
|
|
|
if (th.getComponentOrientation().isLeftToRight()) {
|
|
newIndex = leftArrow ? ui.selectPreviousColumn(doIt)
|
|
: ui.selectNextColumn(doIt);
|
|
} else {
|
|
newIndex = leftArrow ? ui.selectNextColumn(doIt)
|
|
: ui.selectPreviousColumn(doIt);
|
|
}
|
|
|
|
if (newIndex != oldIndex) {
|
|
if (doIt) {
|
|
th.getColumnModel().moveColumn(oldIndex, newIndex);
|
|
} else {
|
|
return true; // we'd do the move if asked
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
private void resize(boolean leftArrow, JTableHeader th,
|
|
BasicTableHeaderUI ui) {
|
|
int columnIndex = ui.getSelectedColumnIndex();
|
|
TableColumn resizingColumn =
|
|
th.getColumnModel().getColumn(columnIndex);
|
|
|
|
th.setResizingColumn(resizingColumn);
|
|
int oldWidth = resizingColumn.getWidth();
|
|
int newWidth = oldWidth;
|
|
|
|
if (th.getComponentOrientation().isLeftToRight()) {
|
|
newWidth = newWidth + (leftArrow ? -1 : 1);
|
|
} else {
|
|
newWidth = newWidth + (leftArrow ? 1 : -1);
|
|
}
|
|
|
|
ui.changeColumnWidth(resizingColumn, th, oldWidth, newWidth);
|
|
}
|
|
}
|
|
} // End of Class BasicTableHeaderUI
|