/*
 * @(#)ShapeCapsule.java    2.0  1 February 2002
 *
 * Copyright 2004
 * College of Computer and Information Science
 * Northeastern University
 * Boston, MA  02115
 *
 * The Java Power Tools software may be used for educational
 * purposes as long as this copyright notice is retained intact
 * at the top of all source files.
 *
 * To discuss possible commercial use of this software, 
 * contact Richard Rasala at Northeastern University, 
 * College of Computer and Information Science,
 * 617-373-2462 or rasala@ccs.neu.edu.
 *
 * The Java Power Tools software has been designed and built
 * in collaboration with Viera Proulx and Jeff Raab.
 *
 * Should this software be modified, the words "Modified from 
 * Original" must be included as a comment below this notice.
 *
 * All publication rights are retained.  This software or its 
 * documentation may not be published in any media either
 * in whole or in part without explicit permission.
 *
 * This software was created with support from Northeastern 
 * University and from NSF grant DUE-9950829.
 */

package edu.neu.ccs.gui;

import edu.neu.ccs.gui.*;
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;

/**
 * A <CODE>JComponent</CODE> encapsulating a shape,
 * and acting as its view.
 * 
 * @author  Richard Rasala 
 * @author  Jeff Raab
 * @version 2.2
 * @since   1.1
 */
public class ShapeCapsule extends JComponent {

    /** Encapsulated shape for this component. */
    protected Shape shape = new Rectangle2D.Double(0, 0, 0, 0);
    
    /** Paint used to render the encapsulated shape. */
    protected Paint paint = Color.black;
    
    /**
     * Constructs an encapsulation of the given shape.
     *
     * @param shape the shape to encapsulate
     */
    public ShapeCapsule(Shape shape) {
        this(shape, null);
    }

    /**
     * Constructs an encapsulation of the given shape
     * to be rendered using the given color or paint.
     *
     * @param shape the shape to encapsulate
     * @param color the color or paint of the shape
     */
    public ShapeCapsule(Shape shape, Paint color) {
        setPaint(color);
        setShape(shape);
    }

    ////////////////
    // Public API //
    ////////////////
    
    /**
     * Sets the encapsulated shape for this component
     * to a clone of the given shape.
     *
     * If the given shape is <CODE>null</CODE>,
     * the encapsulated shape is not changed.
     *
     * @param s the shape to be encapsulated
     * @see #getShape()
     */
    public void setShape(Shape s) {
        if (s == null)
            return;
            
        shape = new GeneralPath(s);
            
        // synchronize the component and shape bounds
        Rectangle bounds = shape.getBounds();
        Insets insets = getInsets();
        super.setBounds(
            bounds.x - insets.left, 
            bounds.y - insets.top, 
            bounds.width  + insets.left + insets.right, 
            bounds.height + insets.top  + insets.bottom);
        
        setPreferredSize(new Dimension(bounds.width, bounds.height));

        repaint();
    }
    
    /** 
     * Returns a clone of the encapsulated shape. 
     *
     * @see #setShape(Shape)
     */
    public Shape getShape() {
        return new GeneralPath(shape);
    }
    
    /**
     * Sets the color or paint used to render this component
     * to the given color or paint.
     *
     * @param color the color or paint to use for rendering
     * @see #getPaint()
     */
    public void setPaint(Paint color) {
        paint = color;
    }
    
    /** 
     * Returns the color or paint used to render this component. 
     *
     * @see #setPaint(Paint)
     */
    public Paint getPaint() {
        return paint;
    }
    
    /**
     * Sets the absolute location of this component
     * to the given location.
     *
     * @param location the location for this component
     * @see #setLocation(int, int)
     */
    public void setLocation(Point location) {
        setBounds(location.x, location.y, getWidth(), getHeight());
    }
    
    /**
     * Sets the absolute location of this component
     * to the given coordinate location.
     *
     * @param x the x-coordinate of the location
     * @param y the y-coordinate of the location
     * @see #setLocation(Point)
     */
    public void setLocation(int x, int y) {
        setBounds(x, y, getWidth(), getHeight());
    }
    
    /**
     * Sets the size of this component
     * to the given size.
     *
     * @param size the size for this component
     * @see #setSize(int, int)
     */
    public void setSize(Dimension size) {
        setBoundsImpl(getX(), getY(), size.width, size.height);
    }
    
    /**
     * Sets the size of this component
     * to the given width and height.
     *
     * @param width the width for this component
     * @param height the height for this component
     * @see #setSize(Dimension)
     */
    public void setSize(int width, int height) {
        setBoundsImpl(getX(), getY(), width, height);
    }
    
    /**
     * Sets the location and size of this component
     * to the location and size of the given rectangle.
     *
     * @param bounds the bounds for this component
     * @see #setBounds(int, int, int, int)
     */
    public void setBounds(Rectangle bounds) {
        setBoundsImpl(
            bounds.x, bounds.y, bounds.width, bounds.height);
    }
    
    /**
     * Sets the location and size of this component
     * to the given coordinate location, width, and height.
     *
     * @param x the x-coordinate of the location
     * @param y the y-coordinate of the location
     * @param width the width for this component
     * @param height the height for this component
     * @see #setBounds(Rectangle)
     */
    public void setBounds(int x, int y, int width, int height) {
        setBoundsImpl(x, y, width, height);
    }

    ///////////////////////
    // Protected methods //
    ///////////////////////

    /**
     * Paints this component to the given graphics context.
     *
     * @param g the graphics context to which to paint
     */
    protected void paintComponent(Graphics g) {
        Graphics2D g2 = (Graphics2D)g;

        // install antialiasing
        Object oldHint = g2.getRenderingHint(
            RenderingHints.KEY_ANTIALIASING);
        g2.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            RenderingHints.VALUE_ANTIALIAS_ON);
        
        // clone and translate the shape
        GeneralPath path = new GeneralPath(getShape());
        Rectangle bounds = path.getBounds();
        Insets insets = getInsets();
        path.transform(AffineTransform.getTranslateInstance(
            0 - (bounds.x + insets.left), 
            0 - (bounds.y + insets.top)));

        // paint the shape
        g2.setPaint(getPaint());
        g2.fill(path);
        
        // uninstall antialiasing
        g2.setRenderingHint(
            RenderingHints.KEY_ANTIALIASING,
            oldHint);
    }
    
    /////////////////////
    // Private methods //
    /////////////////////
    
    /** 
     * Sets the bounds of this component. 
     *
     * @param x the x-coordinate for this component
     * @param y the y-coordinate for this component
     * @param width the width for this component
     * @param height the height for this component
     */
    private void setBoundsImpl(int x, int y, int width, int height) {
        GeneralPath path = new GeneralPath(shape);
        Rectangle bounds = path.getBounds();
        
        // translate shape to origin
        path.transform(AffineTransform.getTranslateInstance(
            -bounds.x, -bounds.y));
            
        // scale shape to new size
        path.transform(AffineTransform.getScaleInstance(
            (double)width  / bounds.width, 
            (double)height / bounds.height));
        
        // translate shape to location
        path.transform(AffineTransform.getTranslateInstance(
            x, y));
            
        // update shape in this capsule
        setShape(path);
    }
}

