/*
* Copyright (c) 2005, 2011, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code 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 General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package javax.swing.plaf.nimbus;
import javax.swing.border.Border;
import javax.swing.JComponent;
import java.awt.Insets;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.RenderingHints;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
/**
* LoweredBorder - A recessed rounded inner shadowed border. Used as the
* standard Nimbus TitledBorder. This class is both a painter and a swing
* border.
*
* @author Jasper Potts
*/
class LoweredBorder extends AbstractRegionPainter implements Border {
private static final int IMG_SIZE = 30;
private static final int RADIUS = 13;
private static final Insets INSETS = new Insets(10,10,10,10);
private static final PaintContext PAINT_CONTEXT = new PaintContext(INSETS,
new Dimension(IMG_SIZE,IMG_SIZE),false,
PaintContext.CacheMode.NINE_SQUARE_SCALE,
Integer.MAX_VALUE, Integer.MAX_VALUE);
// =========================================================================
// Painter Methods
@Override
protected Object[] getExtendedCacheKeys(JComponent c) {
return (c != null)
? new Object[] { c.getBackground() }
: null;
}
/**
* Actually performs the painting operation. Subclasses must implement this
* method. The graphics object passed may represent the actual surface being
* rendererd to, or it may be an intermediate buffer. It has also been
* pre-translated. Simply render the component as if it were located at 0, 0
* and had a width of <code>width</code> and a height of
* <code>height</code>. For performance reasons, you may want to read the
* clip from the Graphics2D object and only render within that space.
*
* @param g The Graphics2D surface to paint to
* @param c The JComponent related to the drawing event. For example,
* if the region being rendered is Button, then <code>c</code>
* will be a JButton. If the region being drawn is
* ScrollBarSlider, then the component will be JScrollBar.
* This value may be null.
* @param width The width of the region to paint. Note that in the case of
* painting the foreground, this value may differ from
* c.getWidth().
* @param height The height of the region to paint. Note that in the case of
* painting the foreground, this value may differ from
* c.getHeight().
*/
protected void doPaint(Graphics2D g, JComponent c, int width, int height,
Object[] extendedCacheKeys) {
Color color = (c == null) ? Color.BLACK : c.getBackground();
BufferedImage img1 = new BufferedImage(IMG_SIZE,IMG_SIZE,
BufferedImage.TYPE_INT_ARGB);
BufferedImage img2 = new BufferedImage(IMG_SIZE,IMG_SIZE,
BufferedImage.TYPE_INT_ARGB);
// draw shadow shape
Graphics2D g2 = (Graphics2D)img1.getGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(color);
g2.fillRoundRect(2,0,26,26,RADIUS,RADIUS);
g2.dispose();
// draw shadow
InnerShadowEffect effect = new InnerShadowEffect();
effect.setDistance(1);
effect.setSize(3);
effect.setColor(getLighter(color, 2.1f));
effect.setAngle(90);
effect.applyEffect(img1,img2,IMG_SIZE,IMG_SIZE);
// draw outline to img2
g2 = (Graphics2D)img2.getGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setClip(0,28,IMG_SIZE,1);
g2.setColor(getLighter(color, 0.90f));
g2.drawRoundRect(2,1,25,25,RADIUS,RADIUS);
g2.dispose();
// draw final image
if (width != IMG_SIZE || height != IMG_SIZE){
ImageScalingHelper.paint(g,0,0,width,height,img2, INSETS, INSETS,
ImageScalingHelper.PaintType.PAINT9_STRETCH,
ImageScalingHelper.PAINT_ALL);
} else {
g.drawImage(img2,0,0,c);
}
img1 = null;
img2 = null;
}
/**
* <p>Gets the PaintContext for this painting operation. This method is
* called on every paint, and so should be fast and produce no garbage. The
* PaintContext contains information such as cache hints. It also contains
* data necessary for decoding points at runtime, such as the stretching
* insets, the canvas size at which the encoded points were defined, and
* whether the stretching insets are inverted.</p>
* <p/>
* <p> This method allows for subclasses to package the painting of
* different states with possibly different canvas sizes, etc, into one
* AbstractRegionPainter implementation.</p>
*
* @return a PaintContext associated with this paint operation.
*/
protected PaintContext getPaintContext() {
return PAINT_CONTEXT;
}
// =========================================================================
// Border Methods
/**
* Returns the insets of the border.
*
* @param c the component for which this border insets value applies
*/
public Insets getBorderInsets(Component c) {
return (Insets) INSETS.clone();
}
/**
* Returns whether or not the border is opaque. If the border is opaque, it
* is responsible for filling in it's own background when painting.
*/
public boolean isBorderOpaque() {
return false;
}
/**
* Paints the border for the specified component with the specified position
* and size.
*
* @param c the component for which this border is being painted
* @param g the paint graphics
* @param x the x position of the painted border
* @param y the y position of the painted border
* @param width the width of the painted border
* @param height the height of the painted border
*/
public void paintBorder(Component c, Graphics g, int x, int y, int width,
int height) {
JComponent comp = (c instanceof JComponent)?(JComponent)c:null;
if (g instanceof Graphics2D){
Graphics2D g2 = (Graphics2D)g;
g2.translate(x,y);
paint(g2,comp, width, height);
g2.translate(-x,-y);
} else {
BufferedImage img = new BufferedImage(IMG_SIZE,IMG_SIZE,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = (Graphics2D)img.getGraphics();
paint(g2,comp, width, height);
g2.dispose();
ImageScalingHelper.paint(g,x,y,width,height,img,INSETS, INSETS,
ImageScalingHelper.PaintType.PAINT9_STRETCH,
ImageScalingHelper.PAINT_ALL);
}
}
private Color getLighter(Color c, float factor){
return new Color(Math.min((int)(c.getRed()/factor), 255),
Math.min((int)(c.getGreen()/factor), 255),
Math.min((int)(c.getBlue()/factor), 255));
}
}