/*
* $Id$
*
* Copyright 2009 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
package org.jdesktop.swingx.renderer;
import java.awt.Component;
import java.awt.Graphics2D;
import java.awt.Point;
import java.util.logging.Logger;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.ComponentAdapter;
import org.jdesktop.swingx.decorator.PainterHighlighter;
import org.jdesktop.swingx.painter.AbstractLayoutPainter;
import org.jdesktop.swingx.painter.AbstractLayoutPainter.HorizontalAlignment;
import org.jdesktop.swingx.painter.Painter;
@SuppressWarnings("unchecked")
public class RelativePainterHighlighter extends PainterHighlighter {
// the fixed value to compare against (should be Comparable)
private Relativizer relativizer;
public RelativePainterHighlighter() {
this(null);
}
public RelativePainterHighlighter(Painter delegate) {
super(delegate);
}
public void setHorizontalAlignment(HorizontalAlignment align) {
getPainter().setHorizontalAlignment(align);
fireStateChanged();
}
public HorizontalAlignment getHorizontalAlignment() {
return getPainter().getHorizontalAlignment();
}
/**
* @param maxValue the maxValue to set
*/
public void setRelativizer(Relativizer relativizer) {
this.relativizer = relativizer;
fireStateChanged();
}
public Relativizer getRelativizer() {
return relativizer;
}
@Override
protected Component doHighlight(Component component,
ComponentAdapter adapter) {
float xPercent = relativizer.getRelativeValue(adapter);
getPainter().setXFactor(xPercent);
getPainter().setVisible(xPercent != Relativizer.ZERO);
return super.doHighlight(component, adapter);
}
/**
* Overridden to wrap a RelativePainter around the given, if not
* already is of type RelativePainter.
*/
@Override
public void setPainter(Painter painter) {
if (!(painter instanceof RelativePainter)) {
painter = new RelativePainter(painter);
}
super.setPainter(painter);
}
@Override
public RelativePainter getPainter() {
return (RelativePainter) super.getPainter();
}
@Override
protected boolean canHighlight(Component component,
ComponentAdapter adapter) {
return relativizer != null && super.canHighlight(component, adapter);
}
//------------------- Relativizer
public static interface Relativizer {
public static final float ZERO = 0.0f;
public static final float ONE = 1.0f;
/**
* Returns a float in the range of 0.0f to 1.0f inclusive which
* indicates the relative value of the given adapter's value.
*
* @param adapter
* @return
*/
public float getRelativeValue(ComponentAdapter adapter);
}
public static class NumberRelativizer implements Relativizer {
private Number max;
private Number current;
private int valueColumn;
private boolean spread;
public NumberRelativizer(Number max) {
this(max, max);
}
public NumberRelativizer(Number max, Number current) {
this(0, max, current);
}
/**
* @param i
* @param max
* @param current
*/
public NumberRelativizer(int column, Number max, Number current) {
this(column, false, max, current);
}
public NumberRelativizer(int column, boolean spreadColumns, Number max, Number current) {
this.current = current;
this.max = max;
this.valueColumn = column;
this.spread = spreadColumns;
}
@Override
public float getRelativeValue(ComponentAdapter adapter) {
if (getNumber(adapter) == null) {
return ZERO;
}
float value = getNumber(adapter).floatValue();
float limit = Math.min(getCurrent().floatValue(), value);
if (isZero(limit)) {
return ZERO;
}
float percent = limit / getMax().floatValue();
if (!spread) {
return percent;
}
int width = adapter.getComponent().getWidth();
int pixelLocation = (int) (percent * width);
int visualColumn = getColumnAt(adapter, pixelLocation);
if (adapter.column < visualColumn) {
return ONE;
} else if (adapter.column > visualColumn) {
return ZERO;
}
int visualColumnWidth = getColumnWidth(adapter, visualColumn);
int startColumn = getColumnLocation(adapter, visualColumn);
int valueWidth = pixelLocation - startColumn;
return (float) valueWidth / (float) visualColumnWidth;
}
/**
* @param adapter
* @return
*/
protected Number getNumber(ComponentAdapter adapter) {
if (adapter.getValue() instanceof Number)
return (Number) adapter.getValue(valueColumn);
return null;
}
private int getColumnLocation(ComponentAdapter adapter, int visualColumn) {
if (!(adapter.getComponent() instanceof JXTable)) {
return 0;
}
JXTable table = (JXTable) adapter.getComponent();
// PENDING JW: guard against null header
return table.getTableHeader().getHeaderRect(visualColumn).x;
}
private int getColumnWidth(ComponentAdapter adapter, int visualColumn) {
if (!(adapter.getComponent() instanceof JXTable)) {
return adapter.getComponent().getWidth();
}
JXTable table = (JXTable) adapter.getComponent();
return table.getColumn(visualColumn).getWidth();
}
private int getColumnAt(ComponentAdapter adapter, int pixelLocation) {
if (!(adapter.getComponent() instanceof JXTable)) {
return 0;
}
JXTable table = (JXTable) adapter.getComponent();
// PENDING JW: guard against null header
return table.getTableHeader().columnAtPoint(new Point(pixelLocation, 10));
}
protected Number getCurrent() {
return current;
}
protected Number getMax() {
return max;
}
protected boolean isZero(float limit) {
return Math.abs(limit) < 0.002;
}
protected int getValueColumn() {
return valueColumn;
}
}
//--------- hack around missing size proportional painters
public static class RelativePainter<T> extends AbstractLayoutPainter<T> {
private Painter<? super T> painter;
private double xFactor;
private double yFactor;
private boolean visible;
public RelativePainter() {
this(null);
}
public RelativePainter(Painter<? super T> delegate) {
this.painter = delegate;
}
public RelativePainter(Painter<? super T> delegate, double xPercent) {
this(delegate);
xFactor = xPercent;
}
public void setPainter(Painter<? super T> painter) {
Object old = getPainter();
this.painter = painter;
firePropertyChange("painter", old, getPainter());
}
public Painter<? super T> getPainter() {
return painter;
}
public void setXFactor(double xPercent) {
double old = getXFactor();
this.xFactor = xPercent;
firePropertyChange("xFactor", old, getXFactor());
}
/**
* @return
*/
public double getXFactor() {
return xFactor;
}
public void setYFactor(double yPercent) {
this.yFactor = yPercent;
}
@Override
protected void doPaint(Graphics2D g, T object, int width, int height) {
if (painter == null) return;
// use epsilon
if (xFactor != 0.0) {
int oldWidth = width;
width = (int) (xFactor * width);
if (getHorizontalAlignment() == HorizontalAlignment.RIGHT) {
g.translate(oldWidth - width, 0);
}
}
if (yFactor != 0.0) {
int oldHeight = height;
height = (int) (yFactor * height);
if (getVerticalAlignment() == VerticalAlignment.BOTTOM) {
g.translate(0, oldHeight - height);
}
}
painter.paint(g, object, width, height);
}
/**
* Overridden to take over completely: super does strange things with
* dirty which result in property changes fired during painting.
*/
@Override
public boolean isVisible() {
return visible;
}
/**
* Overridden to take over completely: super does strange things with
* dirty which result in property changes fired during painting.
*/
@Override
public void setVisible(boolean visible) {
if (isVisible() == visible) return;
this.visible = visible;
firePropertyChange("visible", !visible, isVisible());
}
}
@SuppressWarnings("unused")
private static final Logger LOG = Logger
.getLogger(RelativePainterHighlighter.class.getName());
}