/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*	Tiny Look and Feel                                                         *
*                                                                              *
*  (C) Copyright 2003 - 2007 Hans Bickel                                       *
*                                                                              *
*   For licensing information and credits, please refer to the                 *
*   comment in file de.muntjak.tinylookandfeel.TinyLookAndFeel                 *
*                                                                              *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */

package de.muntjak.tinylookandfeel.borders;

import java.awt.Color;
import java.awt.Component;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.Window;
import java.awt.image.BufferedImage;
import java.util.HashMap;

import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.border.AbstractBorder;
import javax.swing.plaf.UIResource;

import de.muntjak.tinylookandfeel.Theme;
import de.muntjak.tinylookandfeel.TinyLookAndFeel;
import de.muntjak.tinylookandfeel.controlpanel.ColorRoutines;
import de.muntjak.tinylookandfeel.controlpanel.SBChooser;

/**
 * TinyFrameBorder
 * 
 * @version 1.0
 * @author Hans Bickel
 */
public class TinyFrameBorder extends AbstractBorder implements UIResource {

	// cache for already painted captions
	private static HashMap cache = new HashMap();
	
	public static Color buttonUpperColor, buttonLowerColor;
	private static TinyFrameBorder onlyInstance;
	private static Robot robot;
	private static boolean robotsSupported = true;
	private Window window;
	private int titleHeight;
    private boolean isActive;
    
    public static TinyFrameBorder getInstance() {
    	if(onlyInstance == null) {
    		onlyInstance = new TinyFrameBorder();
    		
    		if(robot == null && robotsSupported) {
    			try {
					robot = new Robot();
				} catch(Exception ex) {
					robotsSupported = false;
				}
    		}
    	}
    	
    	return onlyInstance;
    }

	public void paintBorder(Component c, Graphics g, int x, int y, int w, int h) {
		window = SwingUtilities.getWindowAncestor(c);
		isActive = window.isActive();
		
		if(window instanceof JFrame) {
			titleHeight = Theme.frameTitleHeight[Theme.derivedStyle[Theme.style]];
		}
		else {
			titleHeight = Theme.frameInternalTitleHeight[Theme.derivedStyle[Theme.style]];
		}

		if(isActive) {
    		g.setColor(Theme.frameBorderColor[Theme.style].getColor());
    	}
    	else {
    		g.setColor(Theme.frameBorderDisabledColor[Theme.style].getColor());
    	}
    	
    	switch(Theme.derivedStyle[Theme.style]) {
			case Theme.TINY_STYLE:
				drawTinyBorder(g, x, y, w, h);
				break;
			case Theme.W99_STYLE:
				drawWinBorder(g, x, y, w, h);
				break;
			case Theme.YQ_STYLE:
				drawXpBorder(g, x, y, w, h);
				break;
		}
		
		Color col = null;
		if(isActive) {
    		col = Theme.frameCaptionColor[Theme.style].getColor();
    	}
    	else {
    		col = Theme.frameCaptionDisabledColor[Theme.style].getColor();
    	}
    	g.setColor(col);
    	
    	switch(Theme.derivedStyle[Theme.style]) {
			case Theme.TINY_STYLE:
				drawTinyCaption(g, x, y, w, h, col);
				break;
			case Theme.W99_STYLE:
				drawWinCaption(g, x, y, w, h, col);
				break;
			case Theme.YQ_STYLE:
				if(TinyLookAndFeel.controlPanelInstantiated) {
					drawXpCaptionNoCache(g, x, y, w, h, col);
				}
				else {
					drawXpCaption(g, x, y, w, h, col);
				}
				break;
		}
	}
	
	private void drawTinyBorder(Graphics g, int x, int y, int w, int h) {
		
	}
	
	private void drawWinBorder(Graphics g, int x, int y, int w, int h) {
		// Border color
		g.drawLine(x, y, x + w - 2, y);
		g.drawLine(x, y + 1, x, y + h - 2);
		g.drawRect(x + 2, y + 2, w - 5, h - 5);
		g.drawRect(x + 3, y + 3, w - 7, h - 7);
		
		g.setColor(Color.WHITE);
		g.drawLine(x + 1, y + 1, x + w - 3, y + 1);
		g.drawLine(x + 1, y + 2, x + 1, y + h - 3);
		
		g.setColor(Theme.frameDarkColor[Theme.style].getColor());
		g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);
		g.drawLine(x + w - 1, y, x + w - 1, y + h - 1);
		
		g.setColor(Theme.frameLightColor[Theme.style].getColor());
		g.drawLine(x + 1, y + h - 2, x + w - 2, y + h - 2);
		g.drawLine(w - 2, y + 1, x + w - 2, y + h - 2);
	}
	
	private void drawXpBorder(Graphics g, int x, int y, int w, int h) {
		// left
		g.drawLine(x, y + 6, x, y + h - 1);
		g.drawLine(x + 2, y + titleHeight, x + 2, y + h - 3);
		// right
		g.drawLine(x + w - 1, y + 6, x + w - 1, y + h - 1);
		g.drawLine(x + w - 3, y + titleHeight, x + w - 3, y + h - 3);
		// bottom
		g.drawLine(x + 2, y + h - 3, x + w - 3, y + h - 3);
		g.drawLine(x, y + h - 1, x + w - 1, y + h - 1);

		if(robot != null) {
			int wx = window.getLocationOnScreen().x - 4;
			int wy = window.getLocationOnScreen().y;

			Rectangle screenRect = new Rectangle(wx, wy, 4, 4);
			g.drawImage(robot.createScreenCapture(screenRect), x, y, null);
			
			wx = window.getLocationOnScreen().x + window.getWidth() + 1;
			screenRect = new Rectangle(wx, wy, 4, 4);
			g.drawImage(robot.createScreenCapture(screenRect), x + w - 4, y, null);
		}
		else {
			g.setColor(Theme.backColor[Theme.style].getColor());
			g.fillRect(0, 0, w, 3);
		}
		
		if(isActive) {
    		g.setColor(Theme.frameCaptionColor[Theme.style].getColor());
    	}
    	else {
    		g.setColor(Theme.frameCaptionDisabledColor[Theme.style].getColor());
    	}
    	// left
		g.drawLine(x + 1, y + titleHeight, x + 1, y + h - 2);
		// right
		g.drawLine(x + w - 2, y + titleHeight, x + w - 2, y + h - 2);
		// bottom
		g.drawLine(x + 1, y + h - 2, x + w - 2, y + h - 2);
		
		Color c = null;
		if(isActive) {
    		c = Theme.frameBorderColor[Theme.style].getColor();
    	}
    	else {
    		c = Theme.frameBorderDisabledColor[Theme.style].getColor();
    	}
    	
    	g.setColor(ColorRoutines.getAlphaColor(c, 82));
    	g.drawLine(x, y + 3, x, y + 3);
    	g.drawLine(x + w - 1, y + 3, x + w - 1, y + 3);
    	g.setColor(ColorRoutines.getAlphaColor(c, 156));
    	g.drawLine(x, y + 4, x, y + 4);
    	g.drawLine(x + w - 1, y + 4, x + w - 1, y + 4);
    	g.setColor(ColorRoutines.getAlphaColor(c, 215));
    	g.drawLine(x, y + 5, x, y + 5);
    	g.drawLine(x + w - 1, y + 5, x + w - 1, y + 5);
	}
	
	private void drawTinyCaption(Graphics g, int x, int y, int w, int h, Color c) {
		
	}
	
	private void drawWinCaption(Graphics g, int x, int y, int w, int h, Color c) {
		int y2 = y + titleHeight - 2;
		int tw = w - 8;
		int maxSat = 56, maxBri = 78;
		if(!isActive) {
			maxSat = 0; maxBri = 50;
		}
		
		int sat = 0, bri = 0;
		int memSat = 0, memBri = 0;
		Color col = c;
		for(int i = 0; i < tw; i++) {	// horizontal
			g.setColor(col);
			g.drawLine(x + 4 + i, y + 4, x + 4 + i, y2);
			
			sat = maxSat * i / tw;
			bri = maxBri * i / tw;
			if((sat != memSat) || (bri != memBri)) {
				memSat = sat;
				memBri = bri;
				col = SBChooser.getAdjustedColor(c, sat, bri);
			}
		}
		
		
		g.setColor(Theme.frameBorderColor[Theme.style].getColor());
		g.drawLine(x + 4, y + titleHeight - 1,
			x + w - 5, y + titleHeight - 1);
	}
	
	private void drawXpCaption(Graphics g, int x, int y, int w, int h, Color c) {
		if(titleHeight == Theme.frameInternalTitleHeight[Theme.derivedStyle[Theme.style]]) {
			drawXpInternalCaption(g, x, y, w, h, c);
			return;
		}
		
		int spread1 = Theme.frameSpreadDarkDisabled[Theme.style];
		int spread2 = Theme.frameSpreadLightDisabled[Theme.style];
		int y2 = y;
		Color borderColor = null;

		if(isActive) {
			borderColor = Theme.frameBorderColor[Theme.style].getColor();
    		spread1 = Theme.frameSpreadDark[Theme.style];
    		spread2 = Theme.frameSpreadLight[Theme.style];
    	}
    	else {
    		borderColor = Theme.frameBorderDisabledColor[Theme.style].getColor();
    	}
		
		// always paint the semi-transparent parts
// 1
		// blend
		g.setColor(ColorRoutines.getAlphaColor(borderColor, 82));
		g.drawLine(x + 3, y2, x + 3, y2);
		g.drawLine(x + w - 4, y2, x + w - 4, y2);
		g.setColor(ColorRoutines.getAlphaColor(borderColor, 156));
		g.drawLine(x + 4, y2, x + 4, y2);
		g.drawLine(x + w - 5, y2, x + w - 5, y2);
		g.setColor(ColorRoutines.getAlphaColor(borderColor, 215));
		g.drawLine(x + 5, y2, x + 5, y2);
		g.drawLine(x + w - 6, y2, x + w - 6, y2);		
		y2 ++;
// 2
		Color c2 = ColorRoutines.darken(c, 4 * spread1);
		g.setColor(c2);
		g.drawLine(x + 3, y2, x + 5, y2);	// left
		g.drawLine(x + w - 6, y2, x + w - 4, y2);	// right
		// blend
		g.setColor(ColorRoutines.getAlphaColor(c2, 139));
		g.drawLine(x + 2, y2, x + 2, y2);
		g.drawLine(x + w - 3, y2, x + w - 3, y2);
		g.setColor(ColorRoutines.getAlphaColor(c2, 23));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		y2 ++;
// 3
		g.setColor(ColorRoutines.lighten(c, 10 * spread2));
		g.drawLine(x + 4, y2, x + 5, y2);	// left
		g.drawLine(x + w - 6, y2, x + w - 5, y2);	// right
		// darker border
		g.setColor(c);
		g.drawLine(x + 3, y2, x + 3, y2);
		g.drawLine(x + w - 4, y2, x + w - 4, y2);		
		c2 = ColorRoutines.darken(c, 6 * spread1);
		g.setColor(c2);
		g.drawLine(x + 2, y2, x + 2, y2);
		g.drawLine(x + w - 3, y2, x + w - 3, y2);
		// blend		
		g.setColor(ColorRoutines.getAlphaColor(c2, 139));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		y2 ++;
// 4
		// darker border
		g.setColor(c);
		g.drawLine(x + 2, y2, x + 2, y2);
		g.drawLine(x + w - 3, y2, x + w - 3, y2);		
		g.setColor(ColorRoutines.darken(c, 6 * spread1));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		// blend from lightest color
		g.setColor(ColorRoutines.lighten(c, 10 * spread2));
		g.drawLine(x + 3, y2, x + 3, y2);
		g.drawLine(x + w - 4, y2, x + w - 4, y2);
		g.setColor(ColorRoutines.lighten(c, 7 * spread2));
		g.drawLine(x + 4, y2, x + 4, y2);
		g.drawLine(x + w - 5, y2, x + w - 5, y2);
		g.setColor(ColorRoutines.lighten(c, 3 * spread2));
		g.drawLine(x + 5, y2, x + 5, y2);
		g.drawLine(x + w - 6, y2, x + w - 6, y2);
		g.setColor(c);
		g.drawLine(x + 6, y2, x + 6, y2);
		g.drawLine(x + w - 7, y2, x + w - 7, y2);
		y2 ++;
// 5
		g.setColor(ColorRoutines.darken(c, 2 * spread1));
		g.drawLine(x + 5, y2, x + 6, y2);	// left
		g.drawLine(x + x + w - 7, y2, x + w - 6, y2);	// right
		// darker border
		g.setColor(ColorRoutines.darken(c, 6 * spread1));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		// blend from lightest color
		g.setColor(ColorRoutines.lighten(c, 10 * spread2));
		g.drawLine(x + 2, y2, x + 2, y2);
		g.drawLine(x + w - 3, y2, x + w - 3, y2);
		g.setColor(ColorRoutines.lighten(c, 5 * spread2));
		g.drawLine(x + 3, y2, x + 3, y2);
		g.drawLine(x + w - 4, y2, x + w - 4, y2);
		g.setColor(c);
		g.drawLine(x + 4, y2, x + 4, y2);
		g.drawLine(x + w - 5, y2, x + w - 5, y2);
		y2 ++;
// 6
		// lighten little
		g.setColor(ColorRoutines.darken(c, 4 * spread1));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		
		// now either paint from cache or create cached image
		CaptionKey key = new CaptionKey(isActive, titleHeight);
		Object value = cache.get(key);
		
		if(value != null) {
			// image is cached - paint and return
			g.drawImage((Image)value,
				x + 6, y, x + w - 6, y + 5,
				0, 0, 1, 5,
				window);
			g.drawImage((Image)value,
				x + 1, y + 5, x + w - 1, y + titleHeight,
				0, 5, 1, titleHeight,
				window);
				
			// store button colors
			buttonUpperColor = ColorRoutines.darken(c, 4 * spread1);
			buttonLowerColor = ColorRoutines.lighten(c, 10 * spread2);
			
			return;
		}

		Image img = new BufferedImage(1, titleHeight, BufferedImage.TYPE_INT_ARGB);
		Graphics imgGraphics = img.getGraphics();
// 1
		imgGraphics.setColor(borderColor);
		imgGraphics.drawLine(0, 0, 1, 0);
// 2
		imgGraphics.setColor(ColorRoutines.darken(c, 4 * spread1));
		imgGraphics.drawLine(0, 1, 1, 1);
// 3
		imgGraphics.setColor(ColorRoutines.lighten(c, 10 * spread2));
		imgGraphics.drawLine(0, 2, 1, 2);
// 4
		imgGraphics.setColor(c);
		imgGraphics.drawLine(0, 3, 1, 3);
// 5
		imgGraphics.setColor(ColorRoutines.darken(c, 2 * spread1));
		imgGraphics.drawLine(0, 4, 1, 4);
// 6
		buttonUpperColor = ColorRoutines.darken(c, 4 * spread1);
		imgGraphics.setColor(buttonUpperColor);
		imgGraphics.drawLine(0, 5, 1, 5);
// 7 - 8
		imgGraphics.setColor(ColorRoutines.darken(c, 4 * spread1));
		imgGraphics.drawLine(0, 6, 1, 6);
		imgGraphics.drawLine(0, 7, 1, 7);
// 9 - 12
		imgGraphics.setColor(ColorRoutines.darken(c, 3 * spread1));
		imgGraphics.drawLine(0, 8, 1, 8);
		imgGraphics.drawLine(0, 9, 1, 9);
		imgGraphics.drawLine(0, 10, 1, 10);
		imgGraphics.drawLine(0, 11, 1, 11);
// 13 - 15
		imgGraphics.setColor(ColorRoutines.darken(c, 2 * spread1));
		imgGraphics.drawLine(0, 12, 1, 12);
		imgGraphics.drawLine(0, 13, 1, 13);
		imgGraphics.drawLine(0, 14, 1, 14);
// 16 - 17
		imgGraphics.setColor(ColorRoutines.darken(c, spread1));
		imgGraphics.drawLine(0, 15, 1, 15);
		imgGraphics.drawLine(0, 16, 1, 16);
// 18 - 19
		imgGraphics.setColor(c);
		imgGraphics.drawLine(0, 17, 1, 17);
		imgGraphics.drawLine(0, 18, 1, 18);
// 20...
		imgGraphics.setColor(ColorRoutines.lighten(c, 2 * spread2));
		imgGraphics.drawLine(0, 19, 1, 19);
		imgGraphics.setColor(ColorRoutines.lighten(c, 4 * spread2));
		imgGraphics.drawLine(0, 20, 1, 20);
		imgGraphics.setColor(ColorRoutines.lighten(c, 5 * spread2));
		imgGraphics.drawLine(0, 21, 1, 21);
		imgGraphics.setColor(ColorRoutines.lighten(c, 6 * spread2));
		imgGraphics.drawLine(0, 22, 1, 22);
		imgGraphics.setColor(ColorRoutines.lighten(c, 8 * spread2));
		imgGraphics.drawLine(0, 23, 1, 23);
		imgGraphics.setColor(ColorRoutines.lighten(c, 9 * spread2));
		imgGraphics.drawLine(0, 24, 1, 24);
		imgGraphics.setColor(ColorRoutines.lighten(c, 10 * spread2));
		imgGraphics.drawLine(0, 25, 1, 25);
// 27
		imgGraphics.setColor(ColorRoutines.lighten(c, 4 * spread2));
		imgGraphics.drawLine(0, 26, 1, 26);
// 28
		imgGraphics.setColor(ColorRoutines.darken(c, 2 * spread1));
		imgGraphics.drawLine(0, 27, 1, 27);
// 29		
		if(isActive) {
    		imgGraphics.setColor(Theme.frameLightColor[Theme.style].getColor());
    	}
    	else {
    		imgGraphics.setColor(Theme.frameLightDisabledColor[Theme.style].getColor());
    	}
		imgGraphics.drawLine(0, 28, 1, 28);

		// dispose of image graphics
		imgGraphics.dispose();
		
		// paint image
		g.drawImage(img,
			x + 6, y, x + w - 6, y + 5,
			0, 0, 1, 5,
			window);
		g.drawImage(img,
			x + 1, y + 5, x + w - 1, y + titleHeight,
			0, 5, 1, titleHeight,
			window);
		
		// add the image to the cache
		cache.put(key, img);
	}
	
	private void drawXpCaptionNoCache(Graphics g, int x, int y, int w, int h, Color c) {
		if(titleHeight == Theme.frameInternalTitleHeight[Theme.derivedStyle[Theme.style]]) {
			drawXpInternalCaptionNoCache(g, x, y, w, h, c);
			return;
		}

		int y2 = y;
		int spread1 = Theme.frameSpreadDarkDisabled[Theme.style];
		int spread2 = Theme.frameSpreadLightDisabled[Theme.style];
		Color borderColor = null;
		
		if(isActive) {
    		borderColor = Theme.frameBorderColor[Theme.style].getColor();
    		spread1 = Theme.frameSpreadDark[Theme.style];
    		spread2 = Theme.frameSpreadLight[Theme.style];
    	}
    	else {
    		borderColor = Theme.frameBorderDisabledColor[Theme.style].getColor();
    	}
// 1
		g.setColor(borderColor);
		g.drawLine(x + 6, y2, x + w - 7, y2);
		// blend
		g.setColor(ColorRoutines.getAlphaColor(borderColor, 82));
		g.drawLine(x + 3, y2, x + 3, y2);
		g.drawLine(x + w - 4, y2, x + w - 4, y2);
		g.setColor(ColorRoutines.getAlphaColor(borderColor, 156));
		g.drawLine(x + 4, y2, x + 4, y2);
		g.drawLine(x + w - 5, y2, x + w - 5, y2);
		g.setColor(ColorRoutines.getAlphaColor(borderColor, 215));
		g.drawLine(x + 5, y2, x + 5, y2);
		g.drawLine(x + w - 6, y2, x + w - 6, y2);		
		y2 ++;
// 2
		Color c2 = ColorRoutines.darken(c, 4 * spread1);
		g.setColor(c2);
		g.drawLine(x + 3, y2, x + w - 4, y2);
		// blend
		g.setColor(ColorRoutines.getAlphaColor(c2, 139));
		g.drawLine(x + 2, y2, x + 2, y2);
		g.drawLine(x + w - 3, y2, x + w - 3, y2);
		g.setColor(ColorRoutines.getAlphaColor(c2, 23));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		y2 ++;
// 3
		g.setColor(ColorRoutines.lighten(c, 10 * spread2));
		g.drawLine(x + 4, y2, x + w - 5, y2);
		// darker border
		g.setColor(c);
		g.drawLine(x + 3, y2, x + 3, y2);
		g.drawLine(x + w - 4, y2, x + w - 4, y2);		
		c2 = ColorRoutines.darken(c, 6 * spread1);
		g.setColor(c2);
		g.drawLine(x + 2, y2, x + 2, y2);
		g.drawLine(x + w - 3, y2, x + w - 3, y2);
		// blend		
		g.setColor(ColorRoutines.getAlphaColor(c2, 139));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		y2 ++;
// 4
		g.setColor(c);
		g.drawLine(x + 7, y2, x + w - 8, y2);
		// darker border
		g.setColor(c);
		g.drawLine(x + 2, y2, x + 2, y2);
		g.drawLine(x + w - 3, y2, x + w - 3, y2);		
		g.setColor(ColorRoutines.darken(c, 6 * spread1));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		// blend from lightest color
		g.setColor(ColorRoutines.lighten(c, 10 * spread2));
		g.drawLine(x + 3, y2, x + 3, y2);
		g.drawLine(x + w - 4, y2, x + w - 4, y2);
		g.setColor(ColorRoutines.lighten(c, 7 * spread2));
		g.drawLine(x + 4, y2, x + 4, y2);
		g.drawLine(x + w - 5, y2, x + w - 5, y2);
		g.setColor(ColorRoutines.lighten(c, 3 * spread2));
		g.drawLine(x + 5, y2, x + 5, y2);
		g.drawLine(x + w - 6, y2, x + w - 6, y2);
		g.setColor(c);
		g.drawLine(x + 6, y2, x + 6, y2);
		g.drawLine(x + w - 7, y2, x + w - 7, y2);
		y2 ++;
// 5
		g.setColor(ColorRoutines.darken(c, 2 * spread1));
		g.drawLine(x + 5, y2, x + w - 6, y2);
		// darker border
		g.setColor(ColorRoutines.darken(c, 6 * spread1));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		// blend from lightest color
		g.setColor(ColorRoutines.lighten(c, 10 * spread2));
		g.drawLine(x + 2, y2, x + 2, y2);
		g.drawLine(x + w - 3, y2, x + w - 3, y2);
		g.setColor(ColorRoutines.lighten(c, 5 * spread2));
		g.drawLine(x + 3, y2, x + 3, y2);
		g.drawLine(x + w - 4, y2, x + w - 4, y2);
		g.setColor(c);
		g.drawLine(x + 4, y2, x + 4, y2);
		g.drawLine(x + w - 5, y2, x + w - 5, y2);
		y2 ++;
// 6
		buttonUpperColor = ColorRoutines.darken(c, 4 * spread1);
		g.setColor(buttonUpperColor);
		g.drawLine(x + 2, y2, x + w - 3, y2);
		// lighten little
		g.setColor(ColorRoutines.darken(c, 4 * spread1));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		y2 ++;
// 7 - 8
		g.setColor(ColorRoutines.darken(c, 4 * spread1));
		g.fillRect(x + 1, y2, w - 2, 2);
		y2 += 2;
// 9 - 12
		g.setColor(ColorRoutines.darken(c, 3 * spread1));
		g.fillRect(x + 1, y2, w - 2, 4);
		y2 += 4;
// 13 - 15
		g.setColor(ColorRoutines.darken(c, 2 * spread1));
		g.fillRect(x + 1, y2, w - 2, 3);
		y2 += 3;
// 16 - 17
		g.setColor(ColorRoutines.darken(c, 1 * spread1));
		g.fillRect(x + 1, y2, w - 2, 2);
		y2 += 2;
// 18 - 19
		g.setColor(c);
		g.fillRect(x + 1, y2, w - 2, 2);
		y2 += 2;
// 20...
		g.setColor(ColorRoutines.lighten(c, 2 * spread2));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
		g.setColor(ColorRoutines.lighten(c, 4 * spread2));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
		g.setColor(ColorRoutines.lighten(c, 5 * spread2));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
		g.setColor(ColorRoutines.lighten(c, 6 * spread2));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
		g.setColor(ColorRoutines.lighten(c, 8 * spread2));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
		g.setColor(ColorRoutines.lighten(c, 9 * spread2));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
		buttonLowerColor = ColorRoutines.lighten(c, 10 * spread2);
		g.setColor(buttonLowerColor);
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
// 27
		g.setColor(ColorRoutines.lighten(c, 4 * spread2));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
// 28
		g.setColor(ColorRoutines.darken(c, 2 * spread1));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
// 29		
		if(isActive) {
    		g.setColor(Theme.frameLightColor[Theme.style].getColor());
    	}
    	else {
    		g.setColor(Theme.frameLightDisabledColor[Theme.style].getColor());
    	}
		g.drawLine(x + 1, y2, x + w - 2, y2);
	}
	
	private void drawXpInternalCaption(Graphics g, int x, int y, int w, int h, Color c) {
		int spread1 = Theme.frameSpreadDarkDisabled[Theme.style];
		int spread2 = Theme.frameSpreadLightDisabled[Theme.style];
		int y2 = y;
		Color borderColor = null;

		if(isActive) {
			borderColor = Theme.frameBorderColor[Theme.style].getColor();
    		spread1 = Theme.frameSpreadDark[Theme.style];
    		spread2 = Theme.frameSpreadLight[Theme.style];
    	}
    	else {
    		borderColor = Theme.frameBorderDisabledColor[Theme.style].getColor();
    	}
    	
    	// always paint the semi-transparent parts
// 1
		// blend
		g.setColor(ColorRoutines.getAlphaColor(borderColor, 82));
		g.drawLine(x + 3, y2, x + 3, y2);
		g.drawLine(x + w - 4, y2, x + w - 4, y2);
		g.setColor(ColorRoutines.getAlphaColor(borderColor, 156));
		g.drawLine(x + 4, y2, x + 4, y2);
		g.drawLine(x + w - 5, y2, x + w - 5, y2);
		g.setColor(ColorRoutines.getAlphaColor(borderColor, 215));
		g.drawLine(x + 5, y2, x + 5, y2);
		g.drawLine(x + w - 6, y2, x + w - 6, y2);		
		y2 ++;
// 2
		Color c2 = ColorRoutines.darken(c, 4 * spread1);
		g.setColor(c2);
		g.drawLine(x + 3, y2, x + 5, y2);	// left
		g.drawLine(x + w - 6, y2, x + w - 4, y2);	// right
		// blend
		g.setColor(ColorRoutines.getAlphaColor(c2, 139));
		g.drawLine(x + 2, y2, x + 2, y2);
		g.drawLine(x + w - 3, y2, x + w - 3, y2);
		g.setColor(ColorRoutines.getAlphaColor(c2, 23));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		y2 ++;
// 3
		g.setColor(ColorRoutines.lighten(c, 10 * spread2));
		g.drawLine(x + 4, y2, x + 5, y2);	// left
		g.drawLine(x + w - 6, y2, x + w - 5, y2);	// right
		// darker border
		g.setColor(c);
		g.drawLine(x + 3, y2, x + 3, y2);
		g.drawLine(x + w - 4, y2, x + w - 4, y2);		
		c2 = ColorRoutines.darken(c, 6 * spread1);
		g.setColor(c2);
		g.drawLine(x + 2, y2, x + 2, y2);
		g.drawLine(x + w - 3, y2, x + w - 3, y2);
		// blend		
		g.setColor(ColorRoutines.getAlphaColor(c2, 139));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		y2 ++;
// 4
		// darker border
		g.setColor(c);
		g.drawLine(x + 2, y2, x + 2, y2);
		g.drawLine(x + w - 3, y2, x + w - 3, y2);		
		g.setColor(ColorRoutines.darken(c, 6 * spread1));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		// blend from lightest color
		g.setColor(ColorRoutines.lighten(c, 10 * spread2));
		g.drawLine(x + 3, y2, x + 3, y2);
		g.drawLine(x + w - 4, y2, x + w - 4, y2);
		g.setColor(ColorRoutines.lighten(c, 7 * spread2));
		g.drawLine(x + 4, y2, x + 4, y2);
		g.drawLine(x + w - 5, y2, x + w - 5, y2);
		g.setColor(ColorRoutines.lighten(c, 3 * spread2));
		g.drawLine(x + 5, y2, x + 5, y2);
		g.drawLine(x + w - 6, y2, x + w - 6, y2);
		g.setColor(c);
		g.drawLine(x + 6, y2, x + 6, y2);
		g.drawLine(x + w - 7, y2, x + w - 7, y2);
		y2 ++;
// 5
		g.setColor(ColorRoutines.darken(c, 2 * spread1));
		g.drawLine(x + 5, y2, x + 6, y2);	// left
		g.drawLine(x + x + w - 7, y2, x + w - 6, y2);	// right
		// darker border
		g.setColor(ColorRoutines.darken(c, 6 * spread1));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		// blend from lightest color
		g.setColor(ColorRoutines.lighten(c, 10 * spread2));
		g.drawLine(x + 2, y2, x + 2, y2);
		g.drawLine(x + w - 3, y2, x + w - 3, y2);
		g.setColor(ColorRoutines.lighten(c, 5 * spread2));
		g.drawLine(x + 3, y2, x + 3, y2);
		g.drawLine(x + w - 4, y2, x + w - 4, y2);
		g.setColor(c);
		g.drawLine(x + 4, y2, x + 4, y2);
		g.drawLine(x + w - 5, y2, x + w - 5, y2);
		y2 ++;
// 6
		// lighten little
		g.setColor(ColorRoutines.darken(c, 4 * spread1));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		
		// now either paint from cache or create cached image
		CaptionKey key = new CaptionKey(isActive, titleHeight);
		Object value = cache.get(key);
		
		if(value != null) {
			// image is cached - paint and return
			g.drawImage((Image)value,
				x + 6, y, x + w - 6, y + 5,
				0, 0, 1, 5,
				window);
			g.drawImage((Image)value,
				x + 1, y + 5, x + w - 1, y + titleHeight,
				0, 5, 1, titleHeight,
				window);
				
			// store button colors
			buttonUpperColor = ColorRoutines.darken(c, 4 * spread1);
			buttonLowerColor = ColorRoutines.lighten(c, 10 * spread2);

			return;
		}

		Image img = new BufferedImage(1, titleHeight, BufferedImage.TYPE_INT_ARGB);
		Graphics imgGraphics = img.getGraphics();
		
// 1
		imgGraphics.setColor(borderColor);
		imgGraphics.drawLine(0, 0, 1, 0);
// 2
		imgGraphics.setColor(ColorRoutines.darken(c, 4 * spread1));
		imgGraphics.drawLine(0, 1, 1, 1);
// 3
		imgGraphics.setColor(ColorRoutines.lighten(c, 10 * spread2));
		imgGraphics.drawLine(0, 2, 1, 2);
// 4
		imgGraphics.setColor(c);
		imgGraphics.drawLine(0, 3, 1, 3);
// 5
		imgGraphics.setColor(ColorRoutines.darken(c, 2 * spread1));
		imgGraphics.drawLine(0, 4, 1, 4);
// 6
		imgGraphics.setColor(ColorRoutines.darken(c, 4 * spread1));
		imgGraphics.drawLine(0, 5, 1, 5);
// 7
		imgGraphics.setColor(ColorRoutines.darken(c, 4 * spread1));
		imgGraphics.drawLine(0, 6, 1, 6);
// 8 - 10
		imgGraphics.setColor(ColorRoutines.darken(c, 3 * spread1));
		imgGraphics.drawLine(0, 7, 1, 7);
		imgGraphics.drawLine(0, 8, 1, 8);
		imgGraphics.drawLine(0, 9, 1, 9);
// 11 - 12
		imgGraphics.setColor(ColorRoutines.darken(c, 2 * spread1));
		imgGraphics.drawLine(0, 10, 1, 10);
		imgGraphics.drawLine(0, 11, 1, 11);
// 13
		imgGraphics.setColor(ColorRoutines.darken(c, spread1));
		imgGraphics.drawLine(0, 12, 1, 12);
// 14 - 15
		imgGraphics.setColor(c);
		imgGraphics.drawLine(0, 13, 1, 13);
		imgGraphics.drawLine(0, 14, 1, 14);
// 16...
		imgGraphics.setColor(ColorRoutines.lighten(c, 2 * spread2));
		imgGraphics.drawLine(0, 15, 1, 15);
		imgGraphics.setColor(ColorRoutines.lighten(c, 4 * spread2));
		imgGraphics.drawLine(0, 16, 1, 16);
		imgGraphics.setColor(ColorRoutines.lighten(c, 5 * spread2));
		imgGraphics.drawLine(0, 17, 1, 17);
		imgGraphics.setColor(ColorRoutines.lighten(c, 6 * spread2));
		imgGraphics.drawLine(0, 18, 1, 18);
		imgGraphics.setColor(ColorRoutines.lighten(c, 8 * spread2));
		imgGraphics.drawLine(0, 19, 1, 19);
		imgGraphics.setColor(ColorRoutines.lighten(c, 9 * spread2));
		imgGraphics.drawLine(0, 20, 1, 20);
		imgGraphics.setColor(ColorRoutines.lighten(c, 10 * spread2));
		imgGraphics.drawLine(0, 21, 1, 21);
// 23
		imgGraphics.setColor(ColorRoutines.lighten(c, 4 * spread2));
		imgGraphics.drawLine(0, 22, 1, 22);
// 24
		imgGraphics.setColor(ColorRoutines.darken(c, 2 * spread1));
		imgGraphics.drawLine(0, 23, 1, 23);
// 25		
		if(isActive) {
    		imgGraphics.setColor(Theme.frameLightColor[Theme.style].getColor());
    	}
    	else {
    		imgGraphics.setColor(Theme.frameLightDisabledColor[Theme.style].getColor());
    	}
		imgGraphics.drawLine(0, 24, 1, 24);
		
		// dispose of image graphics
		imgGraphics.dispose();
		
		// paint image
		g.drawImage(img,
			x + 6, y, x + w - 6, y + 5,
			0, 0, 1, 5,
			window);
		g.drawImage(img,
			x + 1, y + 5, x + w - 1, y + titleHeight,
			0, 5, 1, titleHeight,
			window);
		
		// add the image to the cache
		cache.put(key, img);
	}
	
	private void drawXpInternalCaptionNoCache(Graphics g, int x, int y, int w, int h, Color c) {	
		int y2 = y;
		int spread1 = Theme.frameSpreadDarkDisabled[Theme.style];
		int spread2 = Theme.frameSpreadLightDisabled[Theme.style];
		Color borderColor = null;
		
		if(isActive) {
    		borderColor = Theme.frameBorderColor[Theme.style].getColor();
    		spread1 = Theme.frameSpreadDark[Theme.style];
    		spread2 = Theme.frameSpreadLight[Theme.style];
    	}
    	else {
    		borderColor = Theme.frameBorderDisabledColor[Theme.style].getColor();
    	}
// 1
		g.setColor(borderColor);
		g.drawLine(x + 6, y2, x + w - 7, y2);
		// blend
		g.setColor(ColorRoutines.getAlphaColor(borderColor, 82));
		g.drawLine(x + 3, y2, x + 3, y2);
		g.drawLine(x + w - 4, y2, x + w - 4, y2);
		g.setColor(ColorRoutines.getAlphaColor(borderColor, 156));
		g.drawLine(x + 4, y2, x + 4, y2);
		g.drawLine(x + w - 5, y2, x + w - 5, y2);
		g.setColor(ColorRoutines.getAlphaColor(borderColor, 215));
		g.drawLine(x + 5, y2, x + 5, y2);
		g.drawLine(x + w - 6, y2, x + w - 6, y2);		
		y2 ++;
// 2
		Color c2 = ColorRoutines.darken(c, 4 * spread1);
		g.setColor(c2);
		g.drawLine(x + 3, y2, x + w - 4, y2);
		// blend
		g.setColor(ColorRoutines.getAlphaColor(c2, 139));
		g.drawLine(x + 2, y2, x + 2, y2);
		g.drawLine(x + w - 3, y2, x + w - 3, y2);
		g.setColor(ColorRoutines.getAlphaColor(c2, 23));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		y2 ++;
// 3
		g.setColor(ColorRoutines.lighten(c, 10 * spread2));
		g.drawLine(x + 4, y2, x + w - 5, y2);
		// darker border
		g.setColor(c);
		g.drawLine(x + 3, y2, x + 3, y2);
		g.drawLine(x + w - 4, y2, x + w - 4, y2);		
		c2 = ColorRoutines.darken(c, 6 * spread1);
		g.setColor(c2);
		g.drawLine(x + 2, y2, x + 2, y2);
		g.drawLine(x + w - 3, y2, x + w - 3, y2);
		// blend		
		g.setColor(ColorRoutines.getAlphaColor(c2, 139));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		y2 ++;
// 4
		g.setColor(c);
		g.drawLine(x + 7, y2, x + w - 8, y2);
		// darker border
		g.setColor(c);
		g.drawLine(x + 2, y2, x + 2, y2);
		g.drawLine(x + w - 3, y2, x + w - 3, y2);		
		g.setColor(ColorRoutines.darken(c, 6 * spread1));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		// blend from lightest color
		g.setColor(ColorRoutines.lighten(c, 10 * spread2));
		g.drawLine(x + 3, y2, x + 3, y2);
		g.drawLine(x + w - 4, y2, x + w - 4, y2);
		g.setColor(ColorRoutines.lighten(c, 7 * spread2));
		g.drawLine(x + 4, y2, x + 4, y2);
		g.drawLine(x + w - 5, y2, x + w - 5, y2);
		g.setColor(ColorRoutines.lighten(c, 3 * spread2));
		g.drawLine(x + 5, y2, x + 5, y2);
		g.drawLine(x + w - 6, y2, x + w - 6, y2);
		g.setColor(c);
		g.drawLine(x + 6, y2, x + 6, y2);
		g.drawLine(x + w - 7, y2, x + w - 7, y2);
		y2 ++;
// 5
		g.setColor(ColorRoutines.darken(c, 2 * spread1));
		g.drawLine(x + 5, y2, x + w - 6, y2);
		// darker border
		g.setColor(ColorRoutines.darken(c, 6 * spread1));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		// blend from lightest color
		g.setColor(ColorRoutines.lighten(c, 10 * spread2));
		g.drawLine(x + 2, y2, x + 2, y2);
		g.drawLine(x + w - 3, y2, x + w - 3, y2);
		g.setColor(ColorRoutines.lighten(c, 5 * spread2));
		g.drawLine(x + 3, y2, x + 3, y2);
		g.drawLine(x + w - 4, y2, x + w - 4, y2);
		g.setColor(c);
		g.drawLine(x + 4, y2, x + 4, y2);
		g.drawLine(x + w - 5, y2, x + w - 5, y2);
		y2 ++;
// 6
		g.setColor(ColorRoutines.darken(c, 4 * spread1));
		g.drawLine(x + 2, y2, x + w - 3, y2);
		// lighten little
		g.setColor(ColorRoutines.darken(c, 4 * spread1));
		g.drawLine(x + 1, y2, x + 1, y2);
		g.drawLine(x + w - 2, y2, x + w - 2, y2);
		y2 ++;
// 7
		g.setColor(ColorRoutines.darken(c, 4 * spread1));
		g.fillRect(x + 1, y2, w - 2, 1);
		y2 += 1;
// 8 - 10
		g.setColor(ColorRoutines.darken(c, 3 * spread1));
		g.fillRect(x + 1, y2, w - 2, 3);
		y2 += 3;
// 11 - 12
		g.setColor(ColorRoutines.darken(c, 2 * spread1));
		g.fillRect(x + 1, y2, w - 2, 2);
		y2 += 2;
// 13
		g.setColor(ColorRoutines.darken(c, 1 * spread1));
		g.fillRect(x + 1, y2, w - 2, 1);
		y2 += 1;
// 14 - 15
		g.setColor(c);
		g.fillRect(x + 1, y2, w - 2, 2);
		y2 += 2;
// 16...
		g.setColor(ColorRoutines.lighten(c, 2 * spread2));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
		g.setColor(ColorRoutines.lighten(c, 4 * spread2));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
		g.setColor(ColorRoutines.lighten(c, 5 * spread2));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
		g.setColor(ColorRoutines.lighten(c, 6 * spread2));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
		g.setColor(ColorRoutines.lighten(c, 8 * spread2));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
		g.setColor(ColorRoutines.lighten(c, 9 * spread2));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
		g.setColor(ColorRoutines.lighten(c, 10 * spread2));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
// 23
		g.setColor(ColorRoutines.lighten(c, 4 * spread2));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
// 24
		g.setColor(ColorRoutines.darken(c, 2 * spread1));
		g.drawLine(x + 1, y2, x + w - 2, y2);
		y2 ++;
// 25		
		if(isActive) {
    		g.setColor(Theme.frameLightColor[Theme.style].getColor());
    	}
    	else {
    		g.setColor(Theme.frameLightDisabledColor[Theme.style].getColor());
    	}
		g.drawLine(x + 1, y2, x + w - 2, y2);
	}

    /**
     * 
     * @see javax.swing.border.Border#getBorderInsets(Component)
     */
	public Insets getBorderInsets(Component c) {
		Window w = SwingUtilities.getWindowAncestor(c);
		
		if(w != null && (w instanceof Frame)) {
			Frame f = (Frame)w;

			// if the frame is maximized, the border should not be visible
			if(f.getExtendedState() == (f.getExtendedState() | Frame.MAXIMIZED_BOTH)) {
				return new Insets(0, 0, 0, 0);
			}
		}
		
		return new Insets(0,
			Theme.frameBorderWidth[Theme.derivedStyle[Theme.style]],
			Theme.frameBorderWidth[Theme.derivedStyle[Theme.style]],
			Theme.frameBorderWidth[Theme.derivedStyle[Theme.style]]);
	}
	
	/**
	 * CaptionKey is used as key in the cache HashMap.
	 * Overrides equals() and hashCode().
	 */
	static class CaptionKey {

		private boolean isActive;
		private int titleHeight;

		CaptionKey(boolean isActive, int titleHeight) {
			this.isActive = isActive;
			this.titleHeight = titleHeight;
		}
		
		public boolean equals(Object o) {
			if(o == null) return false;
			if(!(o instanceof CaptionKey)) return false;

			CaptionKey other = (CaptionKey)o;
			
			return isActive == other.isActive &&
				titleHeight == other.titleHeight;
		}
		
		public int hashCode() {
			return (isActive ? 1 : 2) * titleHeight;
		}
	}
}
