// Near Infinity - An Infinity Engine Browser and Editor
// Copyright (C) 2001 - 2005 Jon Olav Hauglid
// See LICENSE.txt for license information
package org.infinity.gui.layeritem;
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.util.EnumMap;
import javax.swing.JLabel;
import javax.swing.SwingConstants;
import org.infinity.resource.Viewable;
/**
* Represents a game resource structure visually as a shape.
*/
public class ShapedLayerItem extends AbstractLayerItem implements LayerItemListener
{
private static final Color DefaultColor = Color.BLACK;
private Shape shape;
private EnumMap<ItemState, Color> strokeColors;
private EnumMap<ItemState, BasicStroke> strokePen;
private EnumMap<ItemState, Color> fillColors;
private JLabel label;
private boolean stroked, filled;
/**
* Initialize object with default settings.
*/
public ShapedLayerItem()
{
this(null, null, null, null, null);
}
/**
* Initialize object with the specified map location.
* @param location Map location
*/
public ShapedLayerItem(Point location)
{
this(location, null, null, null, null);
}
/**
* Initialize object with a specific map location and an associated viewable object.
* @param location Map location
* @param viewable Associated Viewable object
*/
public ShapedLayerItem(Point location, Viewable viewable)
{
this(location, viewable, null, null, null);
}
/**
* Initialize object with a specific map location, associated Viewable and an additional text message.
* @param location Map location
* @param viewable Associated Viewable object
* @param msg An arbitrary text message
*/
public ShapedLayerItem(Point location, Viewable viewable, String msg)
{
this(location, viewable, msg, null, null);
}
/**
* Initialize object with a specific map location, associated Viewable, an additional text message
* and a shape for the visual representation.
* @param location Map location
* @param viewable Associated Viewable object
* @param msg An arbitrary text message
* @param shape The shape to display
*/
public ShapedLayerItem(Point location, Viewable viewable, String msg, Shape shape)
{
this(location, viewable, msg, shape, null);
}
/**
* Initialize object with a specific map location, associated Viewable, an additional text message,
* a shape for the visual representation and a locical center position within the shape.
* @param location Map location
* @param viewable Associated Viewable object
* @param msg An arbitrary text message
* @param shape The shape to display
* @param center Logical center position within the shape
*/
public ShapedLayerItem(Point location, Viewable viewable, String msg, Shape shape, Point center)
{
super(location, viewable, msg);
strokeColors = new EnumMap<ItemState, Color>(ItemState.class);
strokePen = new EnumMap<ItemState, BasicStroke>(ItemState.class);
fillColors = new EnumMap<ItemState, Color>(ItemState.class);
setLayout(new BorderLayout());
label = new ShapeLabel(this);
label.setHorizontalAlignment(SwingConstants.CENTER);
label.setVerticalAlignment(SwingConstants.CENTER);
add(label, BorderLayout.CENTER);
setShape(shape);
setCenterPosition(center);
addLayerItemListener(this);
}
/**
* Returns the associated shape object.
* @return The associated shape object.
*/
public Shape getShape()
{
return shape;
}
/**
* Sets a new shape.
* @param shape The new shape
*/
public void setShape(Shape shape)
{
this.shape = (shape != null) ? shape : new Rectangle();
updateSize();
updateShape();
}
/**
* Sets the logical center of the icon.
* @param center The center position within the icon
*/
public void setCenterPosition(Point center)
{
if (center == null) {
center = new Point(0, 0);
}
if (!getLocationOffset().equals(center)) {
Point distance = new Point(getLocationOffset().x - center.x, getLocationOffset().y - center.y);
setLocationOffset(center);
// updating component location
Point loc = super.getLocation();
setLocation(loc.x + distance.x, loc.y + distance.y);
validate();
}
}
/**
* Returns the polygon's stroke color of the specified visual state.
* @return Stroke color of the specified visual state.
*/
public Color getStrokeColor(ItemState state)
{
if (state == null) {
state = ItemState.NORMAL;
}
switch (state) {
case HIGHLIGHTED:
if (strokeColors.containsKey(ItemState.HIGHLIGHTED))
return strokeColors.get(ItemState.HIGHLIGHTED);
case NORMAL:
if (strokeColors.containsKey(ItemState.NORMAL))
return strokeColors.get(ItemState.NORMAL);
}
return DefaultColor;
}
/**
* Sets the stroke color of the polygon for the specified visual state.
* @param color The stroke color of the polygon for the specified visual state.
*/
public void setStrokeColor(ItemState state, Color color)
{
if (state != null) {
if (color != null) {
strokeColors.put(state, color);
} else {
strokeColors.remove(state);
}
updateShape();
}
}
/**
* Returns the stroke width of the polygon for the specified visual state.
* @param state The visual state to get the stroke with from.
* @return The stroke width in pixels.
*/
public int getStrokeWidth(ItemState state)
{
if (state == null) {
state = ItemState.NORMAL;
}
if (strokePen.containsKey(state)) {
return (int)strokePen.get(state).getLineWidth();
} else {
return 1;
}
}
/**
* Sets the stroke width of the polygon for the specified visual state.
* @param state The visual state to set the stroke with for.
* @param width The stroke width in pixels.
*/
public void setStrokeWidth(ItemState state, int width)
{
if (state != null) {
if (width < 1) {
width = 1;
}
strokePen.put(state, new BasicStroke((float)width));
updateShape();
}
}
/**
* Returns the polygon's fill color of the specified visual state.
* @return Fill color of the specified visual state.
*/
public Color getFillColor(ItemState state)
{
if (state == null) {
state = ItemState.NORMAL;
}
switch (state) {
case HIGHLIGHTED:
if (fillColors.containsKey(ItemState.HIGHLIGHTED))
return fillColors.get(ItemState.HIGHLIGHTED);
case NORMAL:
if (fillColors.containsKey(ItemState.NORMAL))
return fillColors.get(ItemState.NORMAL);
}
return DefaultColor;
}
/**
* Sets the polygon's fill color for the specified visual state.
* @param color The fill color for the specified visual state.
*/
public void setFillColor(ItemState state, Color color)
{
if (state != null) {
if (color != null) {
fillColors.put(state, color);
} else {
fillColors.remove(state);
}
updateShape();
}
}
/**
* Returns whether the polygon should have a stroked outline.
* @return true if the polygon is drawn with a stroked outline, false otherwise.
*/
public boolean getStroked()
{
return stroked;
}
/**
* Specify whether the polygon should be drawn with a stroked outline.
* @param b If true, the polygon will be drawn with a stroked outline
*/
public void setStroked(boolean b)
{
if (b != stroked) {
stroked = b;
updateShape();
}
}
/**
* Returns whether the polygon should be filled with a specific color.
* @return true if the polygon is drawn filled, false otherwise.
*/
public boolean getFilled()
{
return filled;
}
/**
* Specify whether the polygon should be filled with a specific color.
* @param b If true, the polygon will be filled with a specific color.
*/
public void setFilled(boolean b)
{
if (b != filled) {
filled = b;
updateShape();
}
}
// Returns whether the mouse cursor is over the relevant part of the component
@Override
protected boolean isMouseOver(Point pt)
{
if (shape != null) {
return shape.contains(pt);
} else {
return getBounds().contains(pt);
}
}
private void updateSize()
{
if (shape != null) {
Rectangle r = getBounds();
r.setSize(shape.getBounds().getSize());
setPreferredSize(r.getSize());
setBounds(r);
}
}
// Recreates polygons
private void updateShape()
{
label.repaint();
}
//--------------------- Begin Interface LayerItemListener ---------------------
@Override
public void layerItemChanged(LayerItemEvent event)
{
if (event.getSource() == this) {
updateShape();
}
}
//--------------------- End Interface LayerItemListener ---------------------
//----------------------------- INNER CLASSES -----------------------------
// Extended JLabel to draw shapes on the fly
private static class ShapeLabel extends JLabel
{
private final ShapedLayerItem parent;
public ShapeLabel(ShapedLayerItem parent)
{
super();
this.parent = parent;
}
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
if (parent != null) {
ItemState state = parent.getItemState();
Shape shape = parent.shape;
if (state != null && shape != null && !shape.getBounds().isEmpty()) {
Graphics2D g2 = (Graphics2D)g;
if (parent.filled) {
g2.setColor(parent.fillColors.get(state));
g2.fill(shape);
}
if (parent.stroked) {
Object renderHint = g2.getRenderingHint(RenderingHints.KEY_ANTIALIASING);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
if (parent.strokePen.containsKey(state)) {
g2.setStroke(parent.strokePen.get(state));
}
g2.setColor(parent.strokeColors.get(state));
g2.draw(shape);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, renderHint);
}
}
}
}
}
}