/*
* @(#)SynthTableUI.java 1.17 04/07/23
*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
package javax.swing.plaf.synth;
import javax.swing.table.*;
import javax.swing.*;
import javax.swing.event.*;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.TooManyListenersException;
import java.awt.event.*;
import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.text.*;
import javax.swing.border.*;
import javax.swing.plaf.*;
import javax.swing.plaf.basic.*;
import java.util.Date;
import java.util.EventObject;
import javax.swing.text.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import sun.swing.plaf.synth.SynthUI;
/**
* SynthTableUI implementation
*
* @version 1.17, 07/23/04
* @author Philip Milne
*/
class SynthTableUI extends BasicTableUI implements SynthUI,
PropertyChangeListener {
//
// Instance Variables
//
private SynthStyle style;
private boolean useTableColors;
private boolean useUIBorder;
// TableCellRenderer installed on the JTable at the time we're installed,
// cached so that we can reinstall them at uninstallUI time.
private TableCellRenderer dateRenderer;
private TableCellRenderer numberRenderer;
private TableCellRenderer doubleRender;
private TableCellRenderer floatRenderer;
private TableCellRenderer iconRenderer;
private TableCellRenderer imageIconRenderer;
private TableCellRenderer booleanRenderer;
private TableCellRenderer objectRenderer;
//
// The installation/uninstall procedures and support
//
public static ComponentUI createUI(JComponent c) {
return new SynthTableUI();
}
/**
* Initialize JTable 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() {
dateRenderer = installRendererIfPossible(Date.class, null);
numberRenderer = installRendererIfPossible(Number.class, null);
doubleRender = installRendererIfPossible(Double.class, null);
floatRenderer = installRendererIfPossible(Float.class, null);
iconRenderer = installRendererIfPossible(Icon.class, null);
imageIconRenderer = installRendererIfPossible(ImageIcon.class, null);
booleanRenderer = installRendererIfPossible(Boolean.class,
new SynthBooleanTableCellRenderer());
objectRenderer = installRendererIfPossible(Object.class,
new SynthTableCellRenderer());
updateStyle(table);
}
private TableCellRenderer installRendererIfPossible(Class objectClass,
TableCellRenderer renderer) {
TableCellRenderer currentRenderer = table.getDefaultRenderer(
objectClass);
if (currentRenderer instanceof UIResource) {
table.setDefaultRenderer(objectClass, renderer);
}
return currentRenderer;
}
private void updateStyle(JTable c) {
SynthContext context = getContext(c, ENABLED);
SynthStyle oldStyle = style;
style = SynthLookAndFeel.updateStyle(context, this);
if (style != oldStyle) {
context.setComponentState(ENABLED | SELECTED);
Color sbg = table.getSelectionBackground();
if (sbg == null || sbg instanceof UIResource) {
table.setSelectionBackground(style.getColor(
context, ColorType.TEXT_BACKGROUND));
}
Color sfg = table.getSelectionForeground();
if (sfg == null || sfg instanceof UIResource) {
table.setSelectionForeground(style.getColor(
context, ColorType.TEXT_FOREGROUND));
}
context.setComponentState(ENABLED);
Color gridColor = table.getGridColor();
if (gridColor == null || gridColor instanceof UIResource) {
gridColor = (Color)style.get(context, "Table.gridColor");
if (gridColor == null) {
gridColor = style.getColor(context, ColorType.FOREGROUND);
}
table.setGridColor(gridColor);
}
useTableColors = style.getBoolean(context,
"Table.rendererUseTableColors", true);
useUIBorder = style.getBoolean(context,
"Table.rendererUseUIBorder", true);
Object rowHeight = style.get(context, "Table.rowHeight");
if (rowHeight != null) {
LookAndFeel.installProperty(table, "rowHeight", rowHeight);
}
if (oldStyle != null) {
uninstallKeyboardActions();
installKeyboardActions();
}
}
context.dispose();
}
/**
* Attaches listeners to the JTable.
*/
protected void installListeners() {
super.installListeners();
table.addPropertyChangeListener(this);
}
protected void uninstallDefaults() {
table.setDefaultRenderer(Date.class, dateRenderer);
table.setDefaultRenderer(Number.class, numberRenderer);
table.setDefaultRenderer(Double.class, doubleRender);
table.setDefaultRenderer(Float.class, floatRenderer);
table.setDefaultRenderer(Icon.class, iconRenderer);
table.setDefaultRenderer(ImageIcon.class, imageIconRenderer);
table.setDefaultRenderer(Boolean.class, booleanRenderer);
table.setDefaultRenderer(Object.class, objectRenderer);
if (table.getTransferHandler() instanceof UIResource) {
table.setTransferHandler(null);
}
SynthContext context = getContext(table, ENABLED);
style.uninstallDefaults(context);
context.dispose();
style = null;
}
protected void uninstallListeners() {
table.removePropertyChangeListener(this);
super.uninstallListeners();
}
//
// SynthUI
//
public SynthContext getContext(JComponent c) {
return getContext(c, getComponentState(c));
}
private SynthContext getContext(JComponent c, int state) {
return SynthContext.getContext(SynthContext.class, c,
SynthLookAndFeel.getRegion(c), style, state);
}
private Region getRegion(JComponent c) {
return SynthLookAndFeel.getRegion(c);
}
private int getComponentState(JComponent c) {
return SynthLookAndFeel.getComponentState(c);
}
//
// Paint methods and support
//
public void update(Graphics g, JComponent c) {
SynthContext context = getContext(c);
SynthLookAndFeel.update(context, g);
context.getPainter().paintTableBackground(context,
g, 0, 0, c.getWidth(), c.getHeight());
paint(context, g);
context.dispose();
}
public void paintBorder(SynthContext context, Graphics g, int x,
int y, int w, int h) {
context.getPainter().paintTableBorder(context, g, x, y, w, h);
}
public void paint(Graphics g, JComponent c) {
SynthContext context = getContext(c);
paint(context, g);
context.dispose();
}
protected void paint(SynthContext context, Graphics g) {
if (table.getRowCount() <= 0 || table.getColumnCount() <= 0) {
return;
}
Rectangle clip = g.getClipBounds();
Point upperLeft = clip.getLocation();
Point lowerRight = new Point(clip.x + clip.width - 1, clip.y + clip.height - 1);
int rMin = table.rowAtPoint(upperLeft);
int rMax = table.rowAtPoint(lowerRight);
// This should never happen.
if (rMin == -1) {
rMin = 0;
}
// If the table does not have enough rows to fill the view we'll get -1.
// Replace this with the index of the last row.
if (rMax == -1) {
rMax = table.getRowCount()-1;
}
boolean ltr = table.getComponentOrientation().isLeftToRight();
int cMin = table.columnAtPoint(ltr ? upperLeft : lowerRight);
int cMax = table.columnAtPoint(ltr ? lowerRight : upperLeft);
// 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 = table.getColumnCount()-1;
}
// Paint the grid.
paintGrid(context, g, rMin, rMax, cMin, cMax);
// Paint the cells.
paintCells(context, g, rMin, rMax, cMin, cMax);
}
/*
* Paints the grid lines within aRect, using the grid
* color set with setGridColor. Paints vertical lines
* if getShowVerticalLines()
returns true and paints
* horizontal lines if getShowHorizontalLines()
* returns true.
*/
private void paintGrid(SynthContext context, Graphics g, int rMin,
int rMax, int cMin, int cMax) {
g.setColor(table.getGridColor());
Rectangle minCell = table.getCellRect(rMin, cMin, true);
Rectangle maxCell = table.getCellRect(rMax, cMax, true);
Rectangle damagedArea = minCell.union( maxCell );
SynthGraphicsUtils synthG = context.getStyle().getGraphicsUtils(
context);
if (table.getShowHorizontalLines()) {
int tableWidth = damagedArea.x + damagedArea.width;
int y = damagedArea.y;
for (int row = rMin; row <= rMax; row++) {
y += table.getRowHeight(row);
synthG.drawLine(context, "Table.grid",
g, damagedArea.x, y - 1, tableWidth - 1,y - 1);
}
}
if (table.getShowVerticalLines()) {
TableColumnModel cm = table.getColumnModel();
int tableHeight = damagedArea.y + damagedArea.height;
int x;
if (table.getComponentOrientation().isLeftToRight()) {
x = damagedArea.x;
for (int column = cMin; column <= cMax; column++) {
int w = cm.getColumn(column).getWidth();
x += w;
synthG.drawLine(context, "Table.grid", g, x - 1, 0,
x - 1, tableHeight - 1);
}
} else {
x = damagedArea.x + damagedArea.width;
for (int column = cMin; column < cMax; column++) {
int w = cm.getColumn(column).getWidth();
x -= w;
synthG.drawLine(context, "Table.grid", g, x - 1, 0, x - 1,
tableHeight - 1);
}
x -= cm.getColumn(cMax).getWidth();
synthG.drawLine(context, "Table.grid", g, x, 0, x,
tableHeight - 1);
}
}
}
private int viewIndexForColumn(TableColumn aColumn) {
TableColumnModel cm = table.getColumnModel();
for (int column = 0; column < cm.getColumnCount(); column++) {
if (cm.getColumn(column) == aColumn) {
return column;
}
}
return -1;
}
private void paintCells(SynthContext context, Graphics g, int rMin,
int rMax, int cMin, int cMax) {
JTableHeader header = table.getTableHeader();
TableColumn draggedColumn = (header == null) ? null : header.getDraggedColumn();
TableColumnModel cm = table.getColumnModel();
int columnMargin = cm.getColumnMargin();
Rectangle cellRect;
TableColumn aColumn;
int columnWidth;
if (table.getComponentOrientation().isLeftToRight()) {
for(int row = rMin; row <= rMax; row++) {
cellRect = table.getCellRect(row, cMin, false);
for(int column = cMin; column <= cMax; column++) {
aColumn = cm.getColumn(column);
columnWidth = aColumn.getWidth();
cellRect.width = columnWidth - columnMargin;
if (aColumn != draggedColumn) {
paintCell(context, g, cellRect, row, column);
}
cellRect.x += columnWidth;
}
}
} else {
for(int row = rMin; row <= rMax; row++) {
cellRect = table.getCellRect(row, cMin, false);
aColumn = cm.getColumn(cMin);
if (aColumn != draggedColumn) {
columnWidth = aColumn.getWidth();
cellRect.width = columnWidth - columnMargin;
paintCell(context, g, cellRect, row, 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(context, g, cellRect, row, column);
}
}
}
}
// Paint the dragged column if we are dragging.
if (draggedColumn != null) {
paintDraggedArea(context, g, rMin, rMax, draggedColumn, header.getDraggedDistance());
}
// Remove any renderers that may be left in the rendererPane.
rendererPane.removeAll();
}
private void paintDraggedArea(SynthContext context, Graphics g, int rMin, int rMax, TableColumn draggedColumn, int distance) {
int draggedColumnIndex = viewIndexForColumn(draggedColumn);
Rectangle minCell = table.getCellRect(rMin, draggedColumnIndex, true);
Rectangle maxCell = table.getCellRect(rMax, draggedColumnIndex, true);
Rectangle vacatedColumnRect = minCell.union(maxCell);
// Paint a gray well in place of the moving column.
g.setColor(table.getParent().getBackground());
g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
vacatedColumnRect.width, vacatedColumnRect.height);
// Move to the where the cell has been dragged.
vacatedColumnRect.x += distance;
// Fill the background.
g.setColor(context.getStyle().getColor(context, ColorType.BACKGROUND));
g.fillRect(vacatedColumnRect.x, vacatedColumnRect.y,
vacatedColumnRect.width, vacatedColumnRect.height);
SynthGraphicsUtils synthG = context.getStyle().getGraphicsUtils(
context);
// Paint the vertical grid lines if necessary.
if (table.getShowVerticalLines()) {
g.setColor(table.getGridColor());
int x1 = vacatedColumnRect.x;
int y1 = vacatedColumnRect.y;
int x2 = x1 + vacatedColumnRect.width - 1;
int y2 = y1 + vacatedColumnRect.height - 1;
// Left
synthG.drawLine(context, "Table.grid", g, x1-1, y1, x1-1, y2);
// Right
synthG.drawLine(context, "Table.grid", g, x2, y1, x2, y2);
}
for(int row = rMin; row <= rMax; row++) {
// Render the cell value
Rectangle r = table.getCellRect(row, draggedColumnIndex, false);
r.x += distance;
paintCell(context, g, r, row, draggedColumnIndex);
// Paint the (lower) horizontal grid line if necessary.
if (table.getShowHorizontalLines()) {
g.setColor(table.getGridColor());
Rectangle rcr = table.getCellRect(row, draggedColumnIndex, true);
rcr.x += distance;
int x1 = rcr.x;
int y1 = rcr.y;
int x2 = x1 + rcr.width - 1;
int y2 = y1 + rcr.height - 1;
synthG.drawLine(context, "Table.grid", g, x1, y2, x2, y2);
}
}
}
private void paintCell(SynthContext context, Graphics g,
Rectangle cellRect, int row, int column) {
if (table.isEditing() && table.getEditingRow()==row &&
table.getEditingColumn()==column) {
Component component = table.getEditorComponent();
component.setBounds(cellRect);
component.validate();
}
else {
TableCellRenderer renderer = table.getCellRenderer(row, column);
Component component = table.prepareRenderer(renderer, row, column);
rendererPane.paintComponent(g, component, table, cellRect.x,
cellRect.y, cellRect.width, cellRect.height, true);
}
}
public void propertyChange(PropertyChangeEvent event) {
if (SynthLookAndFeel.shouldUpdateStyle(event)) {
updateStyle((JTable)event.getSource());
}
}
private class SynthBooleanTableCellRenderer extends JCheckBox implements
TableCellRenderer {
public SynthBooleanTableCellRenderer() {
super();
setHorizontalAlignment(JLabel.CENTER);
}
public String getName() {
String name = super.getName();
if (name == null) {
return "Table.cellRenderer";
}
return name;
}
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
if (isSelected) {
setForeground(table.getSelectionForeground());
setBackground(table.getSelectionBackground());
}
else {
setForeground(table.getForeground());
setBackground(table.getBackground());
}
setSelected((value != null && ((Boolean)value).booleanValue()));
// NOTE: We don't do this as otherwise the the JCheckBox will
// think it is selected when it may not be. This means JCheckBox
// renderers don't render the selection correctly.
/*
if (!useTableColors && (isSelected || hasFocus)) {
SynthLookAndFeel.setSelectedUI((SynthButtonUI)SynthLookAndFeel.
getUIOfType(getUI(), SynthButtonUI.class),
isSelected, hasFocus, table.isEnabled());
}
else {
SynthLookAndFeel.resetSelectedUI();
}
*/
return this;
}
public void paint(Graphics g) {
super.paint(g);
// Refer to comment above for why this is commented out.
// SynthLookAndFeel.resetSelectedUI();
}
}
private class SynthTableCellRenderer extends DefaultTableCellRenderer {
private Object numberFormat;
private Object dateFormat;
private boolean opaque;
public void setOpaque(boolean isOpaque) {
opaque = isOpaque;
}
public boolean isOpaque() {
return opaque;
}
public String getName() {
String name = super.getName();
if (name == null) {
return "Table.cellRenderer";
}
return name;
}
public void setBorder(Border b) {
if (useUIBorder || b instanceof SynthBorder) {
super.setBorder(b);
}
}
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
if (!useTableColors && (isSelected || hasFocus)) {
SynthLookAndFeel.setSelectedUI((SynthLabelUI)SynthLookAndFeel.
getUIOfType(getUI(), SynthLabelUI.class),
isSelected, hasFocus, table.isEnabled());
}
else {
SynthLookAndFeel.resetSelectedUI();
}
super.getTableCellRendererComponent(table, value, isSelected,
hasFocus, row, column);
setIcon(null);
Class columnClass = table.getColumnClass(column);
configureValue(value, columnClass);
return this;
}
private void configureValue(Object value, Class columnClass) {
if (columnClass == Object.class || columnClass == null) {
setHorizontalAlignment(JLabel.LEADING);
} else if (columnClass == Float.class || columnClass == Double.class) {
if (numberFormat == null) {
numberFormat = NumberFormat.getInstance();
}
setHorizontalAlignment(JLabel.TRAILING);
setText((value == null) ? "" : ((NumberFormat)numberFormat).format(value));
}
else if (columnClass == Number.class) {
setHorizontalAlignment(JLabel.TRAILING);
// Super will have set value.
}
else if (columnClass == Icon.class || columnClass == ImageIcon.class) {
setHorizontalAlignment(JLabel.CENTER);
setIcon((Icon)value);
setText("");
}
else if (columnClass == Date.class) {
if (dateFormat == null) {
dateFormat = DateFormat.getDateInstance();
}
setHorizontalAlignment(JLabel.LEADING);
setText((value == null) ? "" : ((Format)dateFormat).format(value));
}
else {
configureValue(value, columnClass.getSuperclass());
}
}
public void paint(Graphics g) {
super.paint(g);
SynthLookAndFeel.resetSelectedUI();
}
}
}