001: package Schmortopf.FileComponents.View;
002:
003: /**
004: * An Icon, which draws its content using awt/swing
005: * without connection to a file on disk.
006: *
007: * Used for tree's.
008: */
009:
010: import java.util.Vector;
011: import java.awt.*;
012: import java.awt.event.*;
013: import java.awt.font.*;
014: import java.awt.geom.*;
015:
016: import javax.swing.*;
017: import javax.swing.plaf.*;
018: import javax.swing.plaf.metal.*;
019:
020: public class SelfDrawingIcon implements Icon {
021:
022: public static final byte ColorBlack = (byte) 0; // Black -> black or white, depends on background
023: public static final byte ColorOrange = (byte) 1;
024: public static final byte ColorRed = (byte) 2;
025: public static final byte ColorYellow = (byte) 3;
026: public static final byte ColorGreen = (byte) 4;
027: public static final byte ColorBlue = (byte) 5;
028: public static final byte ColorGray = (byte) 6;
029:
030: private String iconText;
031: private int textColorKey;
032:
033: private Color textColor = Color.black;
034:
035: private Shape[] textShapes;
036: private Stroke stroke;
037:
038: private GradientPaint gradientPaint;
039: private GradientPaint gradientPaintSelected;
040:
041: private int height;
042: private int width;
043:
044: private boolean isSelected = false;
045: private Color selectionColor = new Color(122, 122, 122);
046:
047: private Color startColor = Color.white;
048:
049: /**
050: * Creates an icon with the passed text on it.
051: * The textColorKey is must be one of the static keys of this class.
052: * The actual textcolor is adjusted, so that the contrast to
053: * the current background is big enough.
054: */
055: public SelfDrawingIcon(final String iconText, final int textColorKey) {
056: this .iconText = iconText;
057: this .textColorKey = textColorKey;
058: this .textColor = this
059: .createColorWithContrastToBackground(textColorKey);
060:
061: // Make the glyphvector :
062: final Font rawFont = UIManager.getFont("Tree.font");
063:
064: int textPixelSize = rawFont.getSize(); // unit is pixel, not points here.
065: final Font font = new Font(rawFont.getName(), Font.BOLD,
066: textPixelSize);
067: final FontRenderContext frc = new FontRenderContext(
068: new AffineTransform(), true, true);
069: final GlyphVector glyphVector = font.createGlyphVector(frc,
070: this .iconText);
071:
072: double hShift = 0.0;
073: double vShift = 0.0;
074:
075: this .textShapes = new Shape[glyphVector.getNumGlyphs()];
076:
077: // The font metrics has changed a bit since jdk 1.4, so adjust the sizes :
078: String version = System.getProperty("java.version");
079: if (version.compareTo("1.4") < 0) {
080: // jdk 1.3 or less :
081: this .height = textPixelSize + 4;
082: hShift = font.getSize() / 4.0;
083: vShift = font.getSize(); // in pixels, not dots
084: // In jdk1.3 getGlyphOutline() returned the glyph at
085: // the origin of the system, so it has to be translated for each :
086: for (int k = 0; k < glyphVector.getNumGlyphs(); k++) {
087: final Shape glyph = glyphVector.getGlyphOutline(k);
088: Point2D pos = glyphVector.getGlyphPosition(k);
089: AffineTransform trans = AffineTransform
090: .getTranslateInstance(pos.getX() + hShift, pos
091: .getY()
092: + vShift);
093: final Shape translatedGlyph = trans
094: .createTransformedShape(glyph);
095: this .textShapes[k] = translatedGlyph;
096: }
097: } else {
098: // jdk 1.4 or later :
099: this .height = textPixelSize + 4;
100: hShift = font.getSize() / 4; // offset for all
101: vShift = font.getSize(); // in pixels, not dots
102: // In jdk1.4+, getGlyphOutline() returns the glyph at the correct
103: // position inside the glyph vector, so we don't have to shift each :
104: for (int k = 0; k < glyphVector.getNumGlyphs(); k++) {
105: final Shape glyph = glyphVector.getGlyphOutline(k);
106: Point2D pos = glyphVector.getGlyphPosition(k);
107: AffineTransform trans = AffineTransform
108: .getTranslateInstance(hShift, pos.getY()
109: + vShift);
110: final Shape translatedGlyph = trans
111: .createTransformedShape(glyph);
112: this .textShapes[k] = translatedGlyph;
113: }
114: }
115:
116: this .width = 16;
117: if (this .textShapes.length > 0) {
118: final Shape lastShape = this .textShapes[this .textShapes.length - 1];
119: this .width = lastShape.getBounds().x
120: + lastShape.getBounds().width + 4;
121: }
122:
123: // We rasterize the width, so that the icons produce a few
124: // different widths, but not too much, so it looks properly :
125: final int sizeStep = 2 * font.getSize();
126: int min, max;
127: for (int i = 1; i < 4; i++) {
128: min = (i - 1) * sizeStep;
129: max = i * sizeStep;
130: if ((this .width > min) && (this .width <= max)) {
131: this .width = max;
132: }
133: }
134:
135: this .stroke = new BasicStroke(2, // 2 point stroke for shadowing
136: BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL);
137:
138: // Create a gradient from a startcolor to the backgroundcolor :
139: final Color endColor = UIManager
140: .getColor("TextField.background");
141: final int endColorAverage = (endColor.getRed()
142: + endColor.getGreen() + endColor.getBlue()) / 3;
143: this .startColor = null;
144: if (endColorAverage > 122) {
145: this .startColor = new Color(200, 200, 200);
146: } else {
147: this .startColor = new Color(80, 80, 80);
148: }
149: this .gradientPaint = new GradientPaint(0f, 0f, startColor,
150: (float) this .width, 0.0f, endColor);
151: this .gradientPaintSelected = new GradientPaint(0f, 0f,
152: startColor, (float) this .width, 0.0f, endColor);
153:
154: } // Constructor
155:
156: /**
157: * Switch for the attribute isSelected, which
158: * changes some drawing properties.
159: */
160: public void setSelected(final boolean state) {
161: this .isSelected = state;
162: } // setSelected
163:
164: public void setSelectionColor(final Color selectionColor) {
165: this .selectionColor = selectionColor;
166: // change the gradient to an uniform filled paint
167: // with the selectioncolor :
168: this .gradientPaintSelected = new GradientPaint(0f, 0f,
169: this .selectionColor, (float) this .width, 0.0f,
170: this .selectionColor);
171: }
172:
173: /**
174: * Draw the icon at the specified location. Icon implementations
175: * may use the Component argument to get properties useful for
176: * painting, e.g. the foreground or background color.
177: */
178: public void paintIcon(Component c, Graphics g, int x, int y) {
179: final Graphics2D graphics2D = (Graphics2D) g;
180: // Backup:
181: final Color saveColor = graphics2D.getColor();
182: final AffineTransform backupTransform = graphics2D
183: .getTransform();
184: final Stroke backupStroke = graphics2D.getStroke();
185: final Paint savePaint = graphics2D.getPaint();
186:
187: final Object backupAntiAliasRendering = graphics2D
188: .getRenderingHint(RenderingHints.KEY_ANTIALIASING);
189: final Object backupQualityRendering = graphics2D
190: .getRenderingHint(RenderingHints.KEY_RENDERING);
191:
192: // Reconfigure / draw:
193: g.translate(x, y);
194:
195: graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
196: RenderingHints.VALUE_ANTIALIAS_ON);
197: graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING,
198: RenderingHints.VALUE_RENDER_QUALITY);
199:
200: // paint the gradient background :
201:
202: GradientPaint currentGradient = null;
203: if (this .isSelected) {
204: currentGradient = this .gradientPaintSelected;
205: } else {
206: currentGradient = this .gradientPaint;
207: }
208:
209: graphics2D.setPaint(currentGradient);
210: graphics2D.fill(new Rectangle(0, 0, this .getIconWidth(), this
211: .getIconHeight()));
212:
213: graphics2D.setPaint(this .textColor);
214: for (int k = 0; k < this .textShapes.length; k++) {
215: graphics2D.fill(this .textShapes[k]);
216: }
217:
218: // Restore :
219: graphics2D.setTransform(backupTransform);
220: graphics2D.setColor(saveColor);
221: graphics2D.setStroke(backupStroke);
222:
223: graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
224: backupAntiAliasRendering);
225: graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING,
226: backupQualityRendering);
227:
228: graphics2D.setPaint(savePaint);
229: } // paintIcon
230:
231: /**
232: * Returns the icon's width.
233: *
234: * @return an int specifying the fixed width of the icon.
235: */
236: public int getIconWidth() {
237: return this .width;
238: }
239:
240: /**
241: * Returns the icon's height.
242: *
243: * @return an int specifying the fixed height of the icon.
244: */
245: public int getIconHeight() {
246: return this .height;
247: }
248:
249: private Color createColorWithContrastToBackground(
250: final int textColorKey) {
251: final Color background = UIManager
252: .getColor("TextField.background");
253: final int average = (background.getRed()
254: + background.getGreen() + background.getBlue()) / 3;
255: int contrast = 255 - average;
256: if (contrast < 50)
257: contrast = 50; // dont saturate all
258: if (contrast > 200)
259: contrast = 200; // dont saturate all
260: Color c = null;
261: switch (textColorKey) {
262: case ColorBlack:
263: if (average > 122) {
264: c = new Color(0, 0, 0);
265: } else {
266: c = new Color(255, 255, 255);
267: }
268: break;
269: case ColorRed:
270: c = this .createColorFromFractions(0.850, 0.075, 0.075,
271: contrast);
272: break;
273: case ColorOrange:
274: c = this .createColorFromFractions(0.500, 0.350, 0.150,
275: contrast);
276: break;
277: case ColorYellow:
278: c = this .createColorFromFractions(0.450, 0.450, 0.100,
279: contrast);
280: break;
281: case ColorGreen:
282: c = this .createColorFromFractions(0.075, 0.850, 0.075,
283: contrast);
284: break;
285: case ColorBlue:
286: c = this .createColorFromFractions(0.075, 0.075, 0.850,
287: contrast);
288: break;
289: case ColorGray:
290: c = new Color(contrast, contrast, contrast);
291: break;
292: default:
293: c = new Color(30, 30, 30); // should not happen
294: } // case
295: return c;
296: }
297:
298: /**
299: * Creates a color with the passed color fractions, which
300: * has the passed average value.
301: * The sum of the fractions should equal one.
302: */
303: private Color createColorFromFractions(final double rFraction,
304: final double gFraction, final double bFraction,
305: final int grayScaleAverage) {
306: final double avInitial = (rFraction + gFraction + bFraction) / 3.0;
307: double scalingFactor = 1.0;
308: if (avInitial > 1e-10) {
309: scalingFactor = 1.0 * grayScaleAverage / avInitial;
310: }
311: double r = rFraction * scalingFactor;
312: double g = gFraction * scalingFactor;
313: double b = bFraction * scalingFactor;
314: // If we can't reach the fractions, try to hold the average
315: // by transferring from the biggest value. Its only
316: // an approximative correction somehow, not more :
317: if ((rFraction > gFraction) && (rFraction > bFraction)) {
318: if (r > 255.0) {
319: final double halfOver = (r - 255.0) / 2.0;
320: r = 255.0;
321: g += halfOver;
322: b += halfOver;
323: }
324: } else if ((gFraction > rFraction) && (gFraction > bFraction)) {
325: if (g > 255.0) {
326: final double halfOver = (g - 255.0) / 2.0;
327: g = 255.0;
328: r += halfOver;
329: b += halfOver;
330: }
331: } else if ((bFraction > rFraction) && (bFraction > gFraction)) {
332: if (b > 255.0) {
333: final double halfOver = (b - 255.0) / 2.0;
334: b = 255.0;
335: r += halfOver;
336: g += halfOver;
337: }
338: }
339: // chop:
340: if (r > 255.0)
341: r = 255;
342: if (g > 255.0)
343: g = 255;
344: if (b > 255.0)
345: b = 255;
346: return new Color((int) r, (int) g, (int) b);
347: }
348:
349: } // SelfDrawingIcon
|