package com.explodingpixels.macwidgets;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import javax.swing.table.TableCellRenderer;
import com.explodingpixels.macwidgets.plaf.EmphasizedLabelUI;
import com.explodingpixels.painter.MacWidgetsPainter;
import com.explodingpixels.widgets.TableHeaderUtils;
import com.explodingpixels.widgets.TableUtils;
import com.explodingpixels.widgets.WindowUtils;
/**
* A table header renderer for an iTunes style table. Note that this class specifically extends
* {@link javax.swing.JLabel} in order to be compatible with Glazed Lists. Glazed Lists looks for a label in the
* header renderer in order to install the sort icon, if necessary.
*/
public class ITunesTableHeaderRenderer extends JLabel implements TableCellRenderer {
private JTable fTable;
private int fColumnModelIndexBeingPainted = -1;
//private static Color HIGHLIGHT_BORDER_COLOR = new Color(255, 255, 255, 77);
private static Color BORDER_COLOR = new Color(0, 0, 0, 51);
private static Color UNFOCUSED_FONT_COLOR = new Color(0x8f8f8f);
private static Border BORDER = BorderFactory.createCompoundBorder(
BorderFactory.createMatteBorder(0, 0, 1, 0, MacColorUtils.LEOPARD_BORDER_COLOR),
BorderFactory.createCompoundBorder(
BorderFactory.createMatteBorder(0, 0, 0, 1, BORDER_COLOR),
BorderFactory.createEmptyBorder(1, 5, 0, 5)));
private static final int SORT_ICON_INDENT_PIXELS = 6;
private static final Color SORT_ICON_COLOR = new Color(0, 0, 0, 175);
public ITunesTableHeaderRenderer(JTable table) {
fTable = table;
init();
}
private void init() {
MacWidgetFactory.makeEmphasizedLabel(this,
EmphasizedLabelUI.DEFAULT_FOCUSED_FONT_COLOR, UNFOCUSED_FONT_COLOR,
EmphasizedLabelUI.DEFAULT_EMPHASIS_COLOR);
setBorder(BORDER);
// TODO table column re-ordering is not supported at this time.
fTable.getTableHeader().setReorderingAllowed(false);
}
public void setSortDelegate(TableUtils.SortDelegate sortDelegate) {
TableUtils.makeSortable(fTable, sortDelegate);
}
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
// if the given value is an Icon, then use that. otherwise, use the string version of the
// given value.
if (value instanceof Icon) {
setIcon((Icon) value);
setText("");
setHorizontalAlignment(SwingConstants.CENTER);
} else {
setIcon(null);
setText(value.toString());
setFont(fTable.getTableHeader().getFont());
setHorizontalAlignment(SwingConstants.LEFT);
}
// keep the index of the column we're rendering. if the column isn't a value in the model,
// then use -1 as the index.
fColumnModelIndexBeingPainted = 0 <= column && column < fTable.getColumnCount()
? fTable.convertColumnIndexToModel(column) : -1;
return this;
}
@Override
protected void paintComponent(Graphics g) {
Graphics2D graphics2d = (Graphics2D) g.create();
MacWidgetsPainter<Component> painter = getBackgroundPainter();
painter.paint(graphics2d, this, getWidth(), getHeight());
super.paintComponent(g);
paintSortIndicatorIfNecessary(graphics2d);
graphics2d.dispose();
}
private void paintSortIndicatorIfNecessary(Graphics2D graphics2d) {
TableUtils.SortDirection sortDirection = getColumnBeingPaintedSortDirection();
if (sortDirection != TableUtils.SortDirection.NONE) {
paintSortIndicator(graphics2d, sortDirection);
}
}
private void paintSortIndicator(Graphics2D graphics2d,
TableUtils.SortDirection sortDirection) {
Shape sortShape = sortDirection == TableUtils.SortDirection.ASCENDING
? createSortAscendingShape() : createSortDescendingShape();
int x = getWidth() - sortShape.getBounds().width - SORT_ICON_INDENT_PIXELS;
int y = getHeight() / 2 - sortShape.getBounds().height / 2;
graphics2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
graphics2d.translate(x, y);
graphics2d.setColor(SORT_ICON_COLOR);
graphics2d.fill(sortShape);
}
// Utility methods. ///////////////////////////////////////////////////////////////////////////
private boolean isColumnBeingPaintedPressed() {
return TableHeaderUtils.isColumnPressed(fTable.getTableHeader(), fColumnModelIndexBeingPainted);
}
private boolean isColumnBeingPaintedSelected() {
return TableHeaderUtils.isColumnSelected(fTable.getTableHeader(), fColumnModelIndexBeingPainted);
}
private TableUtils.SortDirection getColumnBeingPaintedSortDirection() {
return TableHeaderUtils.getSortDirection(fTable.getTableHeader(), fColumnModelIndexBeingPainted);
}
private MacWidgetsPainter<Component> getBackgroundPainter() {
MacWidgetsPainter<Component> retVal;
boolean windowHasFocus = WindowUtils.isParentWindowFocused(fTable);
boolean isColumnSelected = isColumnBeingPaintedSelected();
boolean isColumnPressed = isColumnBeingPaintedPressed();
// TODO cleanup this logic.
if (!fTable.isEnabled()) {
retVal = MacPainterFactory.createIAppUnpressedUnselectedHeaderPainter();
} else if (windowHasFocus && isColumnPressed && isColumnSelected) {
retVal = MacPainterFactory.createIAppPressedSelectedHeaderPainter();
} else if (windowHasFocus && isColumnPressed) {
retVal = MacPainterFactory.createIAppPressedUnselectedHeaderPainter();
} else if (windowHasFocus && isColumnSelected) {
retVal = MacPainterFactory.createIAppUnpressedSelectedHeaderPainter();
} else {
retVal = MacPainterFactory.createIAppUnpressedUnselectedHeaderPainter();
}
return retVal;
}
private static GeneralPath createSortAscendingShape() {
float width = 7;
float height = 6;
GeneralPath path = new GeneralPath();
path.moveTo(width / 2.0f, 0.0f);
path.lineTo(width, height);
path.lineTo(0.0f, height);
path.lineTo(width / 2.0f, 0.0f);
return path;
}
private static GeneralPath createSortDescendingShape() {
GeneralPath path = createSortAscendingShape();
double centerX = path.getBounds2D().getWidth() / 2.0;
double centerY = path.getBounds2D().getHeight() / 2.0;
path.transform(AffineTransform.getRotateInstance(Math.PI, centerX, centerY));
return path;
}
}