package Schmortopf.FileComponents.View;
/**
* An Icon, which draws its content using awt/swing
* without connection to a file on disk.
*
* Used for tree's.
*/
import java.util.Vector;
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.geom.*;
import javax.swing.*;
import javax.swing.plaf.*;
import javax.swing.plaf.metal.*;
public class SelfDrawingIcon implements Icon
{
public static final byte ColorBlack = (byte)0; // Black -> black or white, depends on background
public static final byte ColorOrange = (byte)1;
public static final byte ColorRed = (byte)2;
public static final byte ColorYellow = (byte)3;
public static final byte ColorGreen = (byte)4;
public static final byte ColorBlue = (byte)5;
public static final byte ColorGray = (byte)6;
private String iconText;
private int textColorKey;
private Color textColor = Color.black;
private Shape[] textShapes;
private Stroke stroke;
private GradientPaint gradientPaint;
private GradientPaint gradientPaintSelected;
private int height;
private int width;
private boolean isSelected = false;
private Color selectionColor = new Color(122,122,122);
private Color startColor = Color.white;
/**
* Creates an icon with the passed text on it.
* The textColorKey is must be one of the static keys of this class.
* The actual textcolor is adjusted, so that the contrast to
* the current background is big enough.
*/
public SelfDrawingIcon( final String iconText,
final int textColorKey )
{
this.iconText = iconText;
this.textColorKey = textColorKey;
this.textColor = this.createColorWithContrastToBackground( textColorKey );
// Make the glyphvector :
final Font rawFont = UIManager.getFont("Tree.font");
int textPixelSize = rawFont.getSize(); // unit is pixel, not points here.
final Font font = new Font( rawFont.getName(), Font.BOLD, textPixelSize );
final FontRenderContext frc = new FontRenderContext( new AffineTransform(),true,true );
final GlyphVector glyphVector = font.createGlyphVector(frc,this.iconText);
double hShift = 0.0;
double vShift = 0.0;
this.textShapes = new Shape[glyphVector.getNumGlyphs()];
// The font metrics has changed a bit since jdk 1.4, so adjust the sizes :
String version = System.getProperty("java.version");
if( version.compareTo("1.4") < 0 )
{
// jdk 1.3 or less :
this.height = textPixelSize + 4;
hShift = font.getSize()/4.0;
vShift = font.getSize(); // in pixels, not dots
// In jdk1.3 getGlyphOutline() returned the glyph at
// the origin of the system, so it has to be translated for each :
for( int k=0; k < glyphVector.getNumGlyphs(); k++ )
{
final Shape glyph = glyphVector.getGlyphOutline(k);
Point2D pos = glyphVector.getGlyphPosition(k);
AffineTransform trans =
AffineTransform.getTranslateInstance( pos.getX() + hShift,
pos.getY() + vShift );
final Shape translatedGlyph = trans.createTransformedShape(glyph);
this.textShapes[k] = translatedGlyph;
}
}
else
{
// jdk 1.4 or later :
this.height = textPixelSize + 4;
hShift = font.getSize()/4; // offset for all
vShift = font.getSize(); // in pixels, not dots
// In jdk1.4+, getGlyphOutline() returns the glyph at the correct
// position inside the glyph vector, so we don't have to shift each :
for( int k=0; k < glyphVector.getNumGlyphs(); k++ )
{
final Shape glyph = glyphVector.getGlyphOutline(k);
Point2D pos = glyphVector.getGlyphPosition(k);
AffineTransform trans =
AffineTransform.getTranslateInstance( hShift,
pos.getY() + vShift );
final Shape translatedGlyph = trans.createTransformedShape(glyph);
this.textShapes[k] = translatedGlyph;
}
}
this.width = 16;
if( this.textShapes.length > 0 )
{
final Shape lastShape = this.textShapes[ this.textShapes.length-1 ];
this.width = lastShape.getBounds().x + lastShape.getBounds().width + 4;
}
// We rasterize the width, so that the icons produce a few
// different widths, but not too much, so it looks properly :
final int sizeStep = 2 * font.getSize();
int min,max;
for( int i=1; i < 4; i++ )
{
min = (i-1) * sizeStep;
max = i * sizeStep;
if( ( this.width > min ) && ( this.width <= max ) )
{
this.width = max;
}
}
this.stroke = new BasicStroke( 2, // 2 point stroke for shadowing
BasicStroke.CAP_BUTT,
BasicStroke.JOIN_BEVEL );
// Create a gradient from a startcolor to the backgroundcolor :
final Color endColor = UIManager.getColor("TextField.background");
final int endColorAverage = (endColor.getRed()+endColor.getGreen()+endColor.getBlue())/3;
this.startColor = null;
if( endColorAverage > 122 )
{
this.startColor = new Color(200,200,200);
}
else
{
this.startColor = new Color(80,80,80);
}
this.gradientPaint = new GradientPaint( 0f,0f,
startColor,
(float)this.width,
0.0f,
endColor );
this.gradientPaintSelected = new GradientPaint( 0f,0f,
startColor,
(float)this.width,
0.0f,
endColor );
} // Constructor
/**
* Switch for the attribute isSelected, which
* changes some drawing properties.
*/
public void setSelected( final boolean state )
{
this.isSelected = state;
} // setSelected
public void setSelectionColor( final Color selectionColor )
{
this.selectionColor = selectionColor;
// change the gradient to an uniform filled paint
// with the selectioncolor :
this.gradientPaintSelected = new GradientPaint( 0f,0f,
this.selectionColor,
(float)this.width,
0.0f,
this.selectionColor );
}
/**
* Draw the icon at the specified location. Icon implementations
* may use the Component argument to get properties useful for
* painting, e.g. the foreground or background color.
*/
public void paintIcon( Component c, Graphics g, int x, int y )
{
final Graphics2D graphics2D = (Graphics2D)g;
// Backup:
final Color saveColor = graphics2D.getColor();
final AffineTransform backupTransform = graphics2D.getTransform();
final Stroke backupStroke = graphics2D.getStroke();
final Paint savePaint = graphics2D.getPaint();
final Object backupAntiAliasRendering = graphics2D.getRenderingHint( RenderingHints.KEY_ANTIALIASING );
final Object backupQualityRendering = graphics2D.getRenderingHint( RenderingHints.KEY_RENDERING );
// Reconfigure / draw:
g.translate(x,y);
graphics2D.setRenderingHint( RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON );
graphics2D.setRenderingHint( RenderingHints.KEY_RENDERING,
RenderingHints.VALUE_RENDER_QUALITY );
// paint the gradient background :
GradientPaint currentGradient = null;
if( this.isSelected )
{
currentGradient = this.gradientPaintSelected;
}
else
{
currentGradient = this.gradientPaint;
}
graphics2D.setPaint( currentGradient );
graphics2D.fill( new Rectangle(0,0,this.getIconWidth(),this.getIconHeight() ) );
graphics2D.setPaint(this.textColor);
for( int k=0; k < this.textShapes.length; k++ )
{
graphics2D.fill( this.textShapes[k] );
}
// Restore :
graphics2D.setTransform( backupTransform );
graphics2D.setColor(saveColor);
graphics2D.setStroke(backupStroke);
graphics2D.setRenderingHint( RenderingHints.KEY_ANTIALIASING,backupAntiAliasRendering );
graphics2D.setRenderingHint( RenderingHints.KEY_RENDERING,backupQualityRendering );
graphics2D.setPaint(savePaint);
} // paintIcon
/**
* Returns the icon's width.
*
* @return an int specifying the fixed width of the icon.
*/
public int getIconWidth()
{
return this.width;
}
/**
* Returns the icon's height.
*
* @return an int specifying the fixed height of the icon.
*/
public int getIconHeight()
{
return this.height;
}
private Color createColorWithContrastToBackground( final int textColorKey )
{
final Color background = UIManager.getColor("TextField.background");
final int average = (background.getRed()+background.getGreen()+background.getBlue())/3;
int contrast = 255-average;
if( contrast < 50 ) contrast = 50; // dont saturate all
if( contrast > 200 ) contrast = 200; // dont saturate all
Color c = null;
switch( textColorKey )
{
case ColorBlack :
if( average > 122 )
{
c = new Color(0,0,0);
} else
{
c = new Color(255,255,255);
}
break;
case ColorRed :
c = this.createColorFromFractions(0.850,0.075,0.075,contrast);
break;
case ColorOrange :
c = this.createColorFromFractions(0.500,0.350,0.150,contrast);
break;
case ColorYellow :
c = this.createColorFromFractions(0.450,0.450,0.100,contrast);
break;
case ColorGreen :
c = this.createColorFromFractions(0.075,0.850,0.075,contrast);
break;
case ColorBlue :
c = this.createColorFromFractions(0.075,0.075,0.850,contrast);
break;
case ColorGray :
c = new Color( contrast,contrast,contrast );
break;
default :
c = new Color(30,30,30); // should not happen
} // case
return c;
}
/**
* Creates a color with the passed color fractions, which
* has the passed average value.
* The sum of the fractions should equal one.
*/
private Color createColorFromFractions( final double rFraction,
final double gFraction,
final double bFraction,
final int grayScaleAverage )
{
final double avInitial = ( rFraction + gFraction + bFraction ) / 3.0;
double scalingFactor = 1.0;
if( avInitial > 1e-10 )
{
scalingFactor = 1.0 * grayScaleAverage / avInitial;
}
double r = rFraction * scalingFactor;
double g = gFraction * scalingFactor;
double b = bFraction * scalingFactor;
// If we can't reach the fractions, try to hold the average
// by transferring from the biggest value. Its only
// an approximative correction somehow, not more :
if( (rFraction > gFraction) && (rFraction > bFraction) )
{
if( r > 255.0 )
{
final double halfOver = (r-255.0)/2.0;
r = 255.0;
g += halfOver;
b += halfOver;
}
}
else
if( (gFraction > rFraction) && (gFraction > bFraction) )
{
if( g > 255.0 )
{
final double halfOver = (g-255.0)/2.0;
g = 255.0;
r += halfOver;
b += halfOver;
}
}
else
if( (bFraction > rFraction) && (bFraction > gFraction) )
{
if( b > 255.0 )
{
final double halfOver = (b-255.0)/2.0;
b = 255.0;
r += halfOver;
g += halfOver;
}
}
// chop:
if( r > 255.0 ) r = 255;
if( g > 255.0 ) g = 255;
if( b > 255.0 ) b = 255;
return new Color( (int)r, (int)g, (int)b );
}
} // SelfDrawingIcon
|