/*
* $Id: IconBorder.java 2528 2007-12-14 13:49:37Z stolis $
*
* Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
* Santa Clara, California 95054, U.S.A. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package com.revolsys.swing.border;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Graphics;
import java.awt.Insets;
import java.awt.Rectangle;
import java.io.Serializable;
import javax.swing.Icon;
import javax.swing.SwingConstants;
import javax.swing.border.Border;
import org.jdesktop.swingx.icon.EmptyIcon;
/**
* {@code IconBorder} creates a border that places an {@code Icon} in the border
* on the horizontal axis. The border does not add any additional insets other
* than the inset required to produce the space for the icon. If additional
* insets are required, users should Construct a new
* {@link javax.swing.border.CompoundBorder compund border}.
* <p>
* This border is useful when attempting to add {@code Icon}s to pre-existing
* components without requiring specialty painting.
*
* @author Amy Fowler
* @author Karl Schaefer
*
* @version 1.1
*/
public class IconBorder implements Border, Serializable {
/**
* An empty icon.
*/
public static final Icon EMPTY_ICON = new EmptyIcon(16, 16);
/**
*
*/
private static final long serialVersionUID = 1L;
private Icon icon;
private final Rectangle iconBounds = new Rectangle();
private int iconPosition;
private int padding;
/**
* Creates an {@code IconBorder} with an empty icon in a trailing position
* with a padding of 4.
*
* @see #EMPTY_ICON
*/
public IconBorder() {
this(null);
}
/**
* Creates an {@code IconBorder} with the specified icon in a trailing
* position with a padding of 1.
*
* @param validIcon
* the icon to set. This may be {@code null} to represent an
* empty icon.
* @see #EMPTY_ICON
*/
public IconBorder(final Icon validIcon) {
this(validIcon, SwingConstants.TRAILING);
}
/**
* Creates an {@code IconBorder} with the specified constraints and a
* padding of 1.
*
* @param validIcon
* the icon to set. This may be {@code null} to represent an
* empty icon.
* @param iconPosition
* the position to place the icon relative to the component
* contents. This must be one of the following
* {@code SwingConstants}:
* <ul>
* <li>{@code LEADING}</li>
* <li>{@code TRAILING}</li>
* <li>{@code EAST}</li>
* <li>{@code WEST}</li>
* </ul>
* @throws IllegalArgumentException
* if {@code iconPosition} is not a valid position.
* @see #EMPTY_ICON
*/
public IconBorder(final Icon validIcon, final int iconPosition) {
this(validIcon, iconPosition, 1);
}
/**
* Creates an {@code IconBorder} with the specified constraints. If
* {@code validIcon} is {@code null}, {@code EMPTY_ICON} is used instead.
* If {@code padding} is negative, then the border does not use padding.
*
* @param validIcon
* the icon to set. This may be {@code null} to represent an
* empty icon.
* @param iconPosition
* the position to place the icon relative to the component
* contents. This must be one of the following
* {@code SwingConstants}:
* <ul>
* <li>{@code LEADING}</li>
* <li>{@code TRAILING}</li>
* <li>{@code EAST}</li>
* <li>{@code WEST}</li>
* </ul>
* @param padding
* the padding to surround the icon with. All non-positive values
* set the padding to 0.
* @throws IllegalArgumentException
* if {@code iconPosition} is not a valid position.
* @see #EMPTY_ICON
*/
public IconBorder(final Icon validIcon, final int iconPosition, final int padding) {
setIcon(validIcon);
setPadding(padding);
setIconPosition(iconPosition);
}
/**
* Returns EAST or WEST depending on the ComponentOrientation and
* the given postion LEADING/TRAILING this method has no effect for other
* position values
*/
private int bidiDecodeLeadingTrailing(final ComponentOrientation c, final int position) {
if (position == SwingConstants.TRAILING) {
if (!c.isLeftToRight()) {
return SwingConstants.WEST;
}
return SwingConstants.EAST;
}
if (position == SwingConstants.LEADING) {
if (c.isLeftToRight()) {
return SwingConstants.WEST;
}
return SwingConstants.EAST;
}
return position;
}
/**
* {@inheritDoc}
*/
@Override
public Insets getBorderInsets(final Component c) {
final int horizontalInset = this.icon.getIconWidth() + 2 * this.padding;
final int iconPosition = bidiDecodeLeadingTrailing(c.getComponentOrientation(),
this.iconPosition);
if (iconPosition == SwingConstants.EAST) {
return new Insets(0, 0, 0, horizontalInset);
}
return new Insets(0, horizontalInset, 0, 0);
}
/**
* Returns the position to place the icon (relative to the component contents).
*
* @return one of the following {@code SwingConstants}:
* <ul>
* <li>{@code LEADING}</li>
* <li>{@code TRAILING}</li>
* <li>{@code EAST}</li>
* <li>{@code WEST}</li>
* </ul>
*/
public int getIconPosition() {
return this.iconPosition;
}
/**
* Gets the padding surrounding the icon.
*
* @return the padding for the icon. This value is guaranteed to be
* nonnegative.
*/
public int getPadding() {
return this.padding;
}
/**
* This border is not opaque.
*
* @return always returns {@code false}
*/
@Override
public boolean isBorderOpaque() {
return true;
}
private boolean isValidPosition(final int position) {
boolean result = false;
switch (position) {
case SwingConstants.LEADING:
case SwingConstants.TRAILING:
case SwingConstants.EAST:
case SwingConstants.WEST:
result = true;
break;
default:
result = false;
}
return result;
}
/**
* {@inheritDoc}
*/
@Override
public void paintBorder(final Component c, final Graphics g, final int x, final int y,
final int width, final int height) {
final int iconPosition = bidiDecodeLeadingTrailing(c.getComponentOrientation(),
this.iconPosition);
if (iconPosition == SwingConstants.NORTH_EAST) {
this.iconBounds.y = y + this.padding;
this.iconBounds.x = x + width - this.padding - this.icon.getIconWidth();
} else if (iconPosition == SwingConstants.EAST) { // EAST
this.iconBounds.y = y + (height - this.icon.getIconHeight()) / 2;
this.iconBounds.x = x + width - this.padding - this.icon.getIconWidth();
} else if (iconPosition == SwingConstants.WEST) {
this.iconBounds.y = y + (height - this.icon.getIconHeight()) / 2;
this.iconBounds.x = x + this.padding;
}
this.iconBounds.width = this.icon.getIconWidth();
this.iconBounds.height = this.icon.getIconHeight();
this.icon.paintIcon(c, g, this.iconBounds.x, this.iconBounds.y);
}
/**
* Sets the icon for this border.
*
* @param validIcon
* the icon to set. This may be {@code null} to represent an
* empty icon.
* @see #EMPTY_ICON
*/
public void setIcon(final Icon validIcon) {
this.icon = validIcon == null ? EMPTY_ICON : validIcon;
}
/**
* Sets the position to place the icon (relative to the component contents).
*
* @param iconPosition must be one of the following {@code SwingConstants}:
* <ul>
* <li>{@code LEADING}</li>
* <li>{@code TRAILING}</li>
* <li>{@code EAST}</li>
* <li>{@code WEST}</li>
* </ul>
* @throws IllegalArgumentException
* if {@code iconPosition} is not a valid position.
*/
public void setIconPosition(final int iconPosition) {
if (!isValidPosition(iconPosition)) {
throw new IllegalArgumentException("Invalid icon position");
}
this.iconPosition = iconPosition;
}
/**
* Sets the padding around the icon.
*
* @param padding
* the padding to set. If {@code padding < 0}, then
* {@code padding} will be set to {@code 0}.
*/
public void setPadding(final int padding) {
this.padding = padding < 0 ? 0 : padding;
}
}