/* * Copyright (c) 2007 BUSINESS OBJECTS SOFTWARE LIMITED * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * * Neither the name of Business Objects nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* * NavEditorPanel.java * Creation date: Oct 18, 2003 * By: Frank Worsley */ package org.openquark.gems.client.utilities; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; import java.awt.Insets; import java.awt.image.BufferedImage; import javax.swing.border.Border; /** * A smooth, partly transparent border with an optional color highlight for the value editors * and the Intellicut panel. * @author Frank Worsley */ public class SmoothHighlightBorder implements Border { /** The size of the border. If you change this you MUST update the getShades() method. */ private static final int SIZE = 8; /** Height of the drag area at the top of the border. */ private static final int TITLE_BAR_HEIGHT = 6; /** The insets of the border without a title bar. */ public static final Insets NORMAL_INSETS = new Insets(SIZE, SIZE, SIZE, SIZE); /** The insets of the border with a title bar. */ private static final Insets TITLE_BAR_INSETS = new Insets(SIZE + TITLE_BAR_HEIGHT, SIZE, SIZE, SIZE); /** The highlight colour to use. */ private final Color highlight; /** Whether to draw a title bar. */ private final boolean drawTitleBar; /** The background colour of the component when the shades were last calculated. */ private Color lastBackground = null; /** The shade colours that were last calculated. */ private Color[] shades; /** * Constructs a new IntellicutPanelBorder with the given highlight colour. * @param highlight the highlight colour * @param drawTitleBar whether to draw a small titlebar at the top of the border */ public SmoothHighlightBorder(Color highlight, boolean drawTitleBar) { if (highlight == null) { throw new NullPointerException(); } this.highlight = highlight; this.drawTitleBar = drawTitleBar; } /** * Calculates the shades of colour we want to use for shading from the component background, * to the highlight colour and then to transparency. The shades are only re-calculated if the * background colour changes. The inner shade is at index 0, the most outer shade at index 7. * @param background the component background colour * @return an array of shade colours from inside to outside */ private Color[] getShades(Color background) { if (shades != null && background.equals(lastBackground)) { return shades; } lastBackground = background; shades = new Color[SIZE]; // Figure out the inital color we fade to. From that color we fade to complete transparency. // If the highlight is a light color we want to fade to a dark color and vice versa. // We say the highlight is a light color if two RGB values are over 128. boolean redOver = highlight.getRed() >= 128; boolean greenOver = highlight.getGreen() >= 128; boolean blueOver = highlight.getBlue() >= 128; int iRed = -1; int iGreen = -1; int iBlue = -1; if ((redOver && greenOver) || (redOver && blueOver) || (greenOver && blueOver)) { iRed = iGreen = iBlue = 50; } else { iRed = iGreen = iBlue = 150; } Color initialFade = new Color(iRed, iGreen, iBlue, highlight.getAlpha() / 2); Color finalFade = new Color(iRed, iGreen, iBlue, 0); // Fade from component background to the higlight color over a 3 pixel range. int rDiff = (highlight.getRed() - background.getRed()) / 3; int gDiff = (highlight.getGreen() - background.getGreen()) / 3; int bDiff = (highlight.getBlue() - background.getBlue()) / 3; int aDiff = (highlight.getAlpha() - background.getAlpha()) / 3; for (int i = 1; i <= 3; i++) { shades[8 - i] = new Color(background.getRed() + rDiff * i, background.getGreen() + gDiff * i, background.getBlue() + bDiff * i, background.getAlpha() + aDiff * i); } // The 4th pixel is the initial fade color. shades[4] = initialFade; // Then fade from inital fade to transparency over 4 pixels. rDiff = (finalFade.getRed() - initialFade.getRed()) / 4; gDiff = (finalFade.getGreen() - initialFade.getGreen()) / 4; bDiff = (finalFade.getBlue() - initialFade.getBlue()) / 4; aDiff = (finalFade.getAlpha() - initialFade.getAlpha()) / 4; for (int i = 1; i <= 4; i++) { shades[4 - i] = new Color(initialFade.getRed() + rDiff * i, initialFade.getGreen() + gDiff * i, initialFade.getBlue() + bDiff * i, initialFade.getAlpha() + aDiff * i); } return shades; } /** * @see javax.swing.border.Border#paintBorder(java.awt.Component, java.awt.Graphics, int, int, int, int) */ public void paintBorder(Component c, Graphics g, int x, int y, int width, int height) { Color[] shades = getShades(c.getBackground()); BufferedImage buffer = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); Graphics bg = buffer.getGraphics(); // Setup some constant values. int cx0 = x + SIZE + 1; int cy0 = y + SIZE + 1; int cx1 = x + width - SIZE - 2; int cy1 = y + height - SIZE - 2; // Setup values that will be manipulated in the loop. int x1 = x + width - 1; int y1 = y + height - 1; int x2 = x + width - 2*SIZE - 1; int y2 = y + height - 2*SIZE - 1; for (int i = 0; i < SIZE; i++) { // We draw the outermost lines first, then move inwards. Color color = shades[i]; bg.setColor(color); // draw top & bottom bg.drawLine(cx0, y, cx1, y); bg.drawLine(cx0, y1, cx1, y1); // draw left & right bg.drawLine(x, cy0, x, cy1); bg.drawLine(x1, cy0, x1, cy1); int arcSize = 2 * (SIZE - i); // draw top-left & right corners bg.drawArc(x, y, arcSize, arcSize, 180, -90); bg.drawArc(x2, y, arcSize, arcSize, 0, 90); // draw bottom-left & right corners bg.drawArc(x, y2, arcSize, arcSize, 180, 90); bg.drawArc(x2, y2, arcSize, arcSize, 0, -90); // update coordinates x++; y++; x1--; y1--; y2++; x2++; } if (drawTitleBar) { // Draw the title bar. bg.setColor(highlight); bg.fillRect(SIZE, SIZE, width - 2 * SIZE, TITLE_BAR_HEIGHT); bg.setColor(shades[SIZE - 1]); bg.drawRect(SIZE - 1, SIZE - 1, width - 2 * SIZE + 1, TITLE_BAR_HEIGHT); } // Draw the image to the main graphics context g.drawImage(buffer, 0, 0, null); bg.dispose(); buffer.flush(); } /** * @see javax.swing.border.Border#getBorderInsets(java.awt.Component) */ public Insets getBorderInsets(Component c) { return drawTitleBar ? (Insets) TITLE_BAR_INSETS.clone() : (Insets) NORMAL_INSETS.clone(); } /** * @see javax.swing.border.Border#isBorderOpaque() */ public boolean isBorderOpaque() { return false; } }