package com.limegroup.gnutella.gui.border;
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import javax.swing.Icon;
import javax.swing.JComponent;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.border.TitledBorder;
/**
* An TitledBorder that also draws an icon.
*
* THIS CLASS IS ONLY TESTED TO LOOK DECENT WITH
* A LEFT-TO-RIGHT LAYOUT AND THE TITLE ON THE TOP.
*/
public class TitledIconBorder extends TitledBorder {
protected Icon icon;
private Point textLoc = new Point();
private Point iconLoc = new Point();
/**
* Space between the edge of the component & the border.
*/
private static final int BORDER_SPACING = 2;
/**
* Space between icon & text.
*/
private static final int ICON_SPACING = 4;
// Horizontal inset of title that is left or right justified
static protected final int TITLE_INSET_H = 8;
/**
* Space between the border & icon and the text & border.
*/
private static final int EMPTY_SPACE = 4;
public TitledIconBorder() {
super(UIManager.getBorder("TitledBorder.border"));
}
public TitledIconBorder(Border border) {
super(border);
}
public void setIcon(Icon icon) {
this.icon = icon;
}
public Icon getIcon() {
return icon;
}
/**
* 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) {
if(icon == null) {
super.paintBorder(c, g, x, y, width, height);
return;
}
Border border = getBorder();
final String title = getTitle();
final int titlePos = getTitlePosition() == DEFAULT_POSITION ?
TOP : getTitlePosition();
if (title == null || title.equals("")) {
if (border != null) {
border.paintBorder(c, g, x, y, width, height);
}
return;
}
Rectangle grooveRect = new Rectangle(x + EDGE_SPACING, y + EDGE_SPACING,
width - (EDGE_SPACING * 2),
height - (EDGE_SPACING * 2));
Font font = g.getFont();
Color color = g.getColor();
g.setFont(getFont(c));
JComponent jc = (c instanceof JComponent) ? (JComponent)c : null;
FontMetrics fm = jc == null ?
Toolkit.getDefaultToolkit().getFontMetrics(font) :
jc.getFontMetrics(font);
int titleHeight = Math.max(icon.getIconHeight(), fm.getHeight());
int descent = fm.getDescent();
int ascent = fm.getAscent();
int diff;
int titleWidth = fm.stringWidth(title) + ICON_SPACING + icon.getIconWidth();
Insets insets;
if (border != null) {
insets = border.getBorderInsets(c);
} else {
insets = new Insets(0, 0, 0, 0);
}
switch (titlePos) {
case ABOVE_TOP:
diff = ascent + descent + (Math.max(EDGE_SPACING,
BORDER_SPACING*2) - EDGE_SPACING);
grooveRect.y += diff;
grooveRect.height -= diff;
textLoc.y = grooveRect.y - (descent + BORDER_SPACING);
iconLoc.y = grooveRect.y + BORDER_SPACING;
break;
case TOP:
diff = Math.max(0, ((ascent/2) + BORDER_SPACING) - EDGE_SPACING);
grooveRect.y += diff;
grooveRect.height -= diff;
textLoc.y = (grooveRect.y - descent) +
(insets.top + ascent + descent)/2;
iconLoc.y = grooveRect.y - (insets.top / 2);
break;
case BELOW_TOP:
textLoc.y = grooveRect.y + insets.top + ascent + BORDER_SPACING;
iconLoc.y = grooveRect.y - insets.top - BORDER_SPACING;
break;
case ABOVE_BOTTOM:
textLoc.y = (grooveRect.y + grooveRect.height) -
(insets.bottom + descent + BORDER_SPACING);
iconLoc.y = grooveRect.y + grooveRect.height + insets.bottom + BORDER_SPACING;
break;
case BOTTOM:
grooveRect.height -= titleHeight/2;
textLoc.y = ((grooveRect.y + grooveRect.height) - descent) +
((ascent + descent) - insets.bottom)/2;
iconLoc.y = grooveRect.y + grooveRect.height + (insets.bottom/2);
break;
case BELOW_BOTTOM:
grooveRect.height -= titleHeight;
textLoc.y = grooveRect.y + grooveRect.height + ascent +
BORDER_SPACING;
iconLoc.y = grooveRect.y + grooveRect.height - BORDER_SPACING;
break;
}
/* // not java 1.1.8 compatable. :(
int justification = getTitleJustification();
if(c.getComponentOrientation().isLeftToRight()) {
if(justification==LEADING ||
justification==DEFAULT_JUSTIFICATION) {
justification = LEFT;
}
else if(justification==TRAILING) {
justification = RIGHT;
}
}
else {
if(justification==LEADING ||
justification==DEFAULT_JUSTIFICATION) {
justification = RIGHT;
}
else if(justification==TRAILING) {
justification = LEFT;
}
}
*/
int justification = LEFT;
switch (justification) {
case LEFT:
iconLoc.x = grooveRect.x + TITLE_INSET_H + insets.left;
textLoc.x = iconLoc.x + icon.getIconWidth() + ICON_SPACING;
break;
case RIGHT:
iconLoc.x = grooveRect.x + grooveRect.width -
(titleWidth + TITLE_INSET_H + insets.right);
textLoc.x = iconLoc.x - icon.getIconWidth() - ICON_SPACING;
break;
case CENTER:
iconLoc.x = grooveRect.x +
((grooveRect.width - titleWidth) / 2);
textLoc.x = iconLoc.x + icon.getIconWidth() + ICON_SPACING;
break;
}
// If title is positioned in middle of border AND its fontsize
// is greater than the border's thickness, we'll need to paint
// the border in sections to leave space for the component's background
// to show through the title.
//
if (border != null) {
if (((titlePos == TOP) &&
(grooveRect.y > textLoc.y - ascent)) ||
(titlePos == BOTTOM &&
(grooveRect.y + grooveRect.height < textLoc.y + descent))) {
Rectangle clipRect = new Rectangle();
// save original clip
Rectangle saveClip = g.getClipBounds();
// paint strip left of text
clipRect.setBounds(saveClip);
if (computeIntersection(clipRect, x, y, iconLoc.x-EMPTY_SPACE-x, height)) {
g.setClip(clipRect);
border.paintBorder(c, g, grooveRect.x, grooveRect.y,
grooveRect.width, grooveRect.height);
}
// paint strip right of text
clipRect.setBounds(saveClip);
if (computeIntersection(clipRect, iconLoc.x+titleWidth+EMPTY_SPACE+2, y,
x+width-(iconLoc.x+titleWidth+EMPTY_SPACE+2), height)) {
g.setClip(clipRect);
border.paintBorder(c, g, grooveRect.x, grooveRect.y,
grooveRect.width, grooveRect.height);
}
if (titlePos == TOP) {
// paint strip below text
clipRect.setBounds(saveClip);
if (computeIntersection(clipRect, iconLoc.x-EMPTY_SPACE, textLoc.y+descent,
titleWidth+(EMPTY_SPACE*2)+2, y+height-textLoc.y-descent)) {
g.setClip(clipRect);
border.paintBorder(c, g, grooveRect.x, grooveRect.y,
grooveRect.width, grooveRect.height);
}
} else { // titlePos == BOTTOM
// paint strip above text
clipRect.setBounds(saveClip);
if (computeIntersection(clipRect, iconLoc.x-EMPTY_SPACE, y,
titleWidth+(EMPTY_SPACE*2)+2, textLoc.y - ascent - y)) {
g.setClip(clipRect);
border.paintBorder(c, g, grooveRect.x, grooveRect.y,
grooveRect.width, grooveRect.height);
}
}
// restore clip
g.setClip(saveClip);
} else {
border.paintBorder(c, g, grooveRect.x, grooveRect.y,
grooveRect.width, grooveRect.height);
}
}
g.setColor(getTitleColor());
if(title.length() > 0)
g.drawString(getTitle(), textLoc.x, textLoc.y);
icon.paintIcon(c, g, iconLoc.x, iconLoc.y-5);
g.setFont(font);
g.setColor(color);
}
private static boolean computeIntersection(Rectangle dest,
int rx, int ry, int rw, int rh) {
int x1 = Math.max(rx, dest.x);
int x2 = Math.min(rx + rw, dest.x + dest.width);
int y1 = Math.max(ry, dest.y);
int y2 = Math.min(ry + rh, dest.y + dest.height);
dest.x = x1;
dest.y = y1;
dest.width = x2 - x1;
dest.height = y2 - y1;
return !(dest.width <= 0 || dest.height <= 0);
}
}