/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Liquid Look and Feel *
* *
* Author, Miroslav Lazarevic *
* *
* For licensing information and credits, please refer to the *
* comment in file com.birosoft.liquid.LiquidLookAndFeel *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/*
* @(#)LiquidTableHeaderUI.java 1.60 03/01/23
*
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package com.birosoft.liquid;
import com.birosoft.liquid.skin.Skin;
import java.awt.*;
import java.awt.event.*;
import java.util.Enumeration;
import javax.swing.*;
import javax.swing.event.*;
import javax.swing.plaf.*;
import javax.swing.table.*;
/**
* BasicTableHeaderUI implementation
*
* @version 1.60 01/23/03
* @author Alan Chung
* @author Philip Milne
*/
public class LiquidTableHeaderUI extends TableHeaderUI {
private static Cursor resizeCursor = Cursor.getPredefinedCursor(Cursor.E_RESIZE_CURSOR);
protected final static int HEADER_HEIGHT = 22;
private int columnSelected = -1;
private Skin skin;
//
// 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;
//
// Factory methods for the Listeners
//
/**
* Creates the mouse listener for the JTable.
*/
protected MouseInputListener createMouseInputListener() {
return new MouseInputHandler();
}
//
// The installation/uninstall procedures and support
//
public static ComponentUI createUI(JComponent h) {
return new LiquidTableHeaderUI();
}
// Installation
public void installUI(JComponent c) {
header = (JTableHeader) c;
rendererPane = new CellRendererPane();
header.add(rendererPane);
installDefaults();
installListeners();
installKeyboardActions();
}
/**
* Initialize JTableHeader properties, e.g. 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");
}
/**
* Attaches listeners to the JTableHeader.
*/
protected void installListeners() {
mouseInputListener = createMouseInputListener();
header.addMouseListener(mouseInputListener);
header.addMouseMotionListener(mouseInputListener);
}
/**
* Register all keyboard actions on the JTableHeader.
*/
protected void installKeyboardActions() {
}
// 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;
}
protected void uninstallKeyboardActions() {
}
//
// 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;
int columnMargin = cm.getColumnMargin();
Rectangle cellRect = header.getHeaderRect(cMin);
TableColumn aColumn;
if (ltr) {
for (int column = cMin; column <= cMax; column++) {
aColumn = cm.getColumn(column);
columnWidth = aColumn.getWidth();
cellRect.width = columnWidth - columnMargin;
if (aColumn != draggedColumn) {
paintCell(g, cellRect, column);
}
cellRect.x += columnWidth;
}
} else {
aColumn = cm.getColumn(cMin);
if (aColumn != draggedColumn) {
columnWidth = aColumn.getWidth();
cellRect.width = columnWidth - columnMargin;
cellRect.x += columnMargin;
paintCell(g, cellRect, cMin);
}
for (int column = cMin + 1; column <= cMax; column++) {
aColumn = cm.getColumn(column);
columnWidth = aColumn.getWidth();
cellRect.width = columnWidth - columnMargin;
cellRect.x -= columnWidth;
if (aColumn != draggedColumn) {
paintCell(g, cellRect, column);
}
}
}
// 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();
}
return renderer.getTableCellRendererComponent(header.getTable(),
aColumn.getHeaderValue(), false, false, -1, columnIndex);
}
private void paintCell(Graphics g, Rectangle cellRect, int columnIndex) {
int index = 0;
Component component = getHeaderRenderer(columnIndex);
if (columnIndex == columnSelected) {
index = 1;
}
getSkin().draw(g, index, cellRect.x, cellRect.y, cellRect.width,
cellRect.height);
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() {
return HEADER_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);
}
public Skin getSkin() {
if (skin == null) {
skin = new Skin("tableheader.png", 8, 4, 13, 4, 10);
}
return skin;
}
/**
* This inner class is marked "public" due to a compiler bug.
* This class should be treated as a "protected" inner class.
* Instantiate it only within subclasses of BasicTableUI.
*/
public class MouseInputHandler implements MouseInputListener {
private int mouseXOffset;
private Cursor otherCursor = resizeCursor;
public void mouseClicked(MouseEvent e) {
}
private boolean canResize(TableColumn column) {
return (column != null) && header.getResizingAllowed() &&
column.getResizable();
}
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) {
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.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;
}
}
}
private void swapCursor() {
Cursor tmp = header.getCursor();
header.setCursor(otherCursor);
otherCursor = tmp;
}
public void mouseMoved(MouseEvent e) {
if (canResize(getResizingColumn(e.getPoint())) != (header.getCursor() == resizeCursor)) {
swapCursor();
}
Point p = e.getPoint();
int index = header.columnAtPoint(p);
if (index != columnSelected) {
columnSelected = index;
header.repaint();
}
}
public void mouseDragged(MouseEvent e) {
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;
}
resizingColumn.setWidth(newWidth);
Container container;
if ((header.getParent() == null) ||
((container = header.getParent().getParent()) == null) ||
!(container instanceof JScrollPane)) {
return;
}
if (!container.getComponentOrientation().isLeftToRight() &&
!headerLeftToRight) {
JTable table = header.getTable();
if (table != null) {
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);
/* Update the original X offset value. */
mouseXOffset += diff;
}
}
}
} 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));
cm.moveColumn(columnIndex, newColumnIndex);
return;
}
}
setDraggedDistance(draggedDistance, columnIndex);
}
}
public void mouseReleased(MouseEvent e) {
setDraggedDistance(0, viewIndexForColumn(header.getDraggedColumn()));
header.setResizingColumn(null);
header.setDraggedColumn(null);
}
public void mouseEntered(MouseEvent e) {
}
public void mouseExited(MouseEvent e) {
columnSelected = -1;
header.repaint();
}
//
// Protected & Private Methods
//
private void setDraggedDistance(int draggedDistance, int column) {
header.setDraggedDistance(draggedDistance);
if (column != -1) {
header.getColumnModel().moveColumn(column, column);
}
}
}
}
// End of Class BasicTableHeaderUI