/*
 * @(#)TextAnchor.java    2.4.0   25 May 2005
 *
 * Copyright 2005
 * 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.*;

import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
import java.awt.font.*;

/**
 * <P>The <CODE>TextAnchor</CODE> class defines interfaces that specify
 * how to compute the location and bounds of a <CODE>String</CODE> that
 * will be displayed using a given <CODE>Font</CODE> in conjunction with
 * a given anchor position.</P>
 *
 * <P>The <CODE>TextAnchor</CODE> class also defines static objects that
 * implement the interfaces it defines.</P>
 *
 * <P>The <CODE>TextAnchor</CODE> class cannot be instantiated.</P>
 *
 * <p>In 2.4.0, this class was updated to be consistent with
 * refinements to the <code>Paintable</code> interface.</p>
 *
 * @author  Richard Rasala
 * @version 2.4.0
 * @since   2.3
 */
public class TextAnchor
{
    /** Private constructor to prevent instantiation. */
    private TextAnchor() {}
    
    
    /**
     * The standard font render context defined using the identity
     * transform, anti-aliasing on, and fractional-metrics off.
     */
    static final FontRenderContext standardFRC = TextPaintable.standardFRC;
    
    
    /**
     * <P>The <CODE>XLocator</CODE> interface defines the methods needed to
     * compute the x-coordinate of the left, right, and center of a given
     * <CODE>String</CODE> displayed in a given <CODE>Font</CODE> using the
     * given anchorX position and this strategy.
     */
    public interface XLocator {
        /**
         * Returns the left x-coordinate of the string.
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorX the x-coordinate of the anchor position
         * @return the left x-coordinate of the string
         */
        public float getLeftX(String string, Font font, float anchorX);
        
        /**
         * Returns the right x-coordinate of the string.
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorX the x-coordinate of the anchor position
         * @return the right x-coordinate of the string
         */
        public float getRightX(String string, Font font, float anchorX);
        
        /**
         * Returns the center x-coordinate of the string.
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorX the x-coordinate of the anchor position
         * @return the center x-coordinate of the string
         */
        public float getCenterX(String string, Font font, float anchorX);
    }
    
    
    /**
     * <P>The <CODE>YLocator</CODE> interface defines the methods needed to
     * compute the y-coordinate of the base line, ascent line, descent line,
     * and leading line of a given <CODE>String</CODE> displayed in a given
     * <CODE>Font</CODE> using the given anchorY position and this strategy.
     */
    public interface YLocator {
        /**
         * Returns the base line y-coordinate of the string.
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorY the y-coordinate of the anchor position
         * @return the base line y-coordinate of the string
         */
        public float getBaseLineY(String string, Font font, float anchorY);
        
        /**
         * Returns the ascent line y-coordinate of the string.
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorY the y-coordinate of the anchor position
         * @return the ascent line y-coordinate of the string
         */
        public float getAscentLineY(String string, Font font, float anchorY);
        
        /**
         * Returns the descent line y-coordinate of the string.
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorY the y-coordinate of the anchor position
         * @return the descent line y-coordinate of the string
         */
        public float getDescentLineY(String string, Font font, float anchorY);
        
        /**
         * Returns the leading line y-coordinate of the string.
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorY the y-coordinate of the anchor position
         * @return the leading line y-coordinate of the string
         */
        public float getLeadingLineY(String string, Font font, float anchorY);
    }
    
    
    /**
     * <P>The <CODE>Locator</CODE> interface defines the methods needed to
     * compute the coordinates, bounds, and center of a <CODE>String</CODE>
     * displayed in a given <CODE>Font</CODE> using the given anchor
     * positions and this strategy.
     */
    public interface Locator {
        /**
         * Returns the left x-coordinate of the string.
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorX the x-coordinate of the anchor position
         * @return the left x-coordinate of the string
         */
        public float getLeftX(String string, Font font, float anchorX);
        
        /**
         * Returns the right x-coordinate of the string.
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorX the x-coordinate of the anchor position
         * @return the right x-coordinate of the string
         */
        public float getRightX(String string, Font font, float anchorX);
        
        /**
         * Returns the center x-coordinate of the string.
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorX the x-coordinate of the anchor position
         * @return the center x-coordinate of the string
         */
        public float getCenterX(String string, Font font, float anchorX);
        
        /**
         * Returns the base line y-coordinate of the string.
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorY the y-coordinate of the anchor position
         * @return the base line y-coordinate of the string
         */
        public float getBaseLineY(String string, Font font, float anchorY);
        
        /**
         * Returns the ascent line y-coordinate of the string.
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorY the y-coordinate of the anchor position
         * @return the ascent line y-coordinate of the string
         */
        public float getAscentLineY(String string, Font font, float anchorY);
        
        /**
         * Returns the descent line y-coordinate of the string.
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorY the y-coordinate of the anchor position
         * @return the descent line y-coordinate of the string
         */
        public float getDescentLineY(String string, Font font, float anchorY);
        
        /**
         * Returns the leading line y-coordinate of the string.
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorY the y-coordinate of the anchor position
         * @return the leading line y-coordinate of the string
         */
        public float getLeadingLineY(String string, Font font, float anchorY);
        
        /**
         * <P>Returns the width of the area that will be occupied by
         * the displayed string.</P>
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorX the x-coordinate of the anchor position
         * @return the width of the displayed string
         */
        public float getWidth
            (String string, Font font, float anchorX);
        
        /**
         * <P>Returns the height of the area that will be occupied by
         * the displayed string.</P>
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorY the y-coordinate of the anchor position
         * @return the height of the displayed string
         */
        public float getHeight
            (String string, Font font, float anchorY);
        
        /**
         * <P>Returns a copy of the 2-dimensional bounds of the area that
         * will be occupied by the displayed string.</P>
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorX the x-coordinate of the anchor position
         * @param anchorY the y-coordinate of the anchor position
         * @return the 2-dimensional bounds of the displayed string
         */
        public XRect getBounds2D
            (String string, Font font, float anchorX, float anchorY);
        
        /**
         * <P>Returns a copy of the logical center of the  displayed string,
         * that is, the point specified by <CODE>getCenterX</CODE> and by
         * <CODE>getBaseLineY</CODE>.
         *
         * @param string  the string to display
         * @param font    the font to use for the display
         * @param anchorX the x-coordinate of the anchor position
         * @param anchorY the y-coordinate of the anchor position
         * @return the 2-dimensional bounds of the displayed string
         */
        public XPoint2D getCenter
            (String string, Font font, float anchorX, float anchorY);
    }
    
    
    /**
     * Returns a <CODE>Locator</CODE> object constructed using the given
     * <CODE>XLocator</CODE> and <CODE>YLocator</CODE> objects.
     *
     * @param xlocator the XLocator object
     * @param ylocator the YLocator object
     * @return the constructed Locator object
     */
    public static Locator makeLocator
        (final XLocator xlocator, final YLocator ylocator)
    {
        return new Locator() {
            public float getLeftX(String string, Font font, float anchorX)
            {
                return xlocator.getLeftX(string, font, anchorX);
            }
            
            public float getRightX(String string, Font font, float anchorX)
            {
                return xlocator.getRightX(string, font, anchorX);
            }
            
            public float getCenterX(String string, Font font, float anchorX)
            {
                return xlocator.getCenterX(string, font, anchorX);
            }
            
            public float getBaseLineY(String string, Font font, float anchorY)
            {
                return ylocator.getBaseLineY(string, font, anchorY);
            }
            
            public float getAscentLineY(String string, Font font, float anchorY)
            {
                return ylocator.getAscentLineY(string, font, anchorY);
            }
            
            public float getDescentLineY(String string, Font font, float anchorY)
            {
                return ylocator.getDescentLineY(string, font, anchorY);
            }
            
            public float getLeadingLineY(String string, Font font, float anchorY)
            {
                return ylocator.getLeadingLineY(string, font, anchorY);
            }
            
            public float getWidth
                (String string, Font font, float anchorX)
            {
                return getRightX(string, font, anchorX)
                    -  getLeftX (string, font, anchorX);
            }
            
            public float getHeight
                (String string, Font font, float anchorY)
            {
                return getLeadingLineY(string, font, anchorY)
                    -  getAscentLineY (string, font, anchorY);
            }
            
            public XRect getBounds2D
                (String string, Font font, float anchorX, float anchorY)
            {
                double x = getLeftX      (string, font, anchorX);
                double y = getAscentLineY(string, font, anchorY);
                
                double w = getRightX      (string, font, anchorX) - x;
                double h = getLeadingLineY(string, font, anchorY) - y;
                
                return new XRect(x, y, w, h);
            }
            
            public XPoint2D getCenter
                (String string, Font font, float anchorX, float anchorY)
            {
                double x = getCenterX  (string, font, anchorX);
                double y = getBaseLineY(string, font, anchorY);
                
                return new XPoint2D(x, y);
            }
        };
    }
    
    
    /**
     * The <CODE>XLocator</CODE> object that corresponds to an anchor
     * at the left edge of the displayed string.
     */
    public static final XLocator LEFT =
        new XLocator() {
            public float getLeftX(String string, Font font, float anchorX)
            {
                return anchorX;
            }
            
            public float getRightX(String string, Font font, float anchorX)
            {
                if ((string == null) || (font == null))
                    return anchorX;
                
                Rectangle2D bounds = font.getStringBounds(string, standardFRC);
                
                return anchorX + (float) (bounds.getWidth());
            }
            
            public float getCenterX(String string, Font font, float anchorX)
            {
                if ((string == null) || (font == null))
                    return anchorX;
                
                Rectangle2D bounds = font.getStringBounds(string, standardFRC);
                
                return anchorX + (float) (bounds.getWidth() / 2);
            }
        };
    
    
    /**
     * The <CODE>XLocator</CODE> object that corresponds to an anchor
     * at the right edge of the displayed string.
     */
    public static final XLocator RIGHT =
        new XLocator() {
            public float getLeftX(String string, Font font, float anchorX)
            {
                if ((string == null) || (font == null))
                    return anchorX;
                
                Rectangle2D bounds = font.getStringBounds(string, standardFRC);
                
                return anchorX - (float) (bounds.getWidth());
            }
            
            public float getRightX(String string, Font font, float anchorX)
            {
                return anchorX;
            }
            
            public float getCenterX(String string, Font font, float anchorX)
            {
                if ((string == null) || (font == null))
                    return anchorX;
                
                Rectangle2D bounds = font.getStringBounds(string, standardFRC);
                
                return anchorX - (float) (bounds.getWidth() / 2);
            }
        };
    
    
    /**
     * The <CODE>XLocator</CODE> object that corresponds to an anchor
     * at the horizontal center of the displayed string.
     */
    public static final XLocator CENTER =
        new XLocator() {
            public float getLeftX(String string, Font font, float anchorX)
            {
                if ((string == null) || (font == null))
                    return anchorX;
                
                Rectangle2D bounds = font.getStringBounds(string, standardFRC);
                
                return anchorX - (float) (bounds.getWidth() / 2);
            }
            
            public float getRightX(String string, Font font, float anchorX)
            {
                if ((string == null) || (font == null))
                    return anchorX;
                
                Rectangle2D bounds = font.getStringBounds(string, standardFRC);
                
                return anchorX + (float) (bounds.getWidth() / 2);
            }
            
            public float getCenterX(String string, Font font, float anchorX)
            {
                return anchorX;
            }
        };
    
    
    /**
     * The <CODE>YLocator</CODE> object that corresponds to an anchor
     * on the base line of the displayed string.
     */
    public static final YLocator BASELINE =
        new YLocator() {
            public float getBaseLineY(String string, Font font, float anchorY)
            {
                return anchorY;
            }
            
            public float getAscentLineY(String string, Font font, float anchorY)
            {
                if ((string == null) || (font == null))
                    return anchorY;
                
                LineMetrics metrics = font.getLineMetrics(string, standardFRC);
                
                return anchorY - metrics.getAscent();
            }
            
            public float getDescentLineY(String string, Font font, float anchorY)
            {
                if ((string == null) || (font == null))
                    return anchorY;
                
                LineMetrics metrics = font.getLineMetrics(string, standardFRC);
                
                return anchorY + metrics.getDescent();
           }
            
            public float getLeadingLineY(String string, Font font, float anchorY)
            {
                if ((string == null) || (font == null))
                    return anchorY;
                
                LineMetrics metrics = font.getLineMetrics(string, standardFRC);
                
                return anchorY + metrics.getDescent() + metrics.getLeading();
            }
            
        };
    
    
    /**
     * The <CODE>YLocator</CODE> object that corresponds to an anchor
     * on the ascent line of the displayed string.
     */
    public static final YLocator ASCENTLINE =
        new YLocator() {
            public float getBaseLineY(String string, Font font, float anchorY)
            {
                if ((string == null) || (font == null))
                    return anchorY;
                
                LineMetrics metrics = font.getLineMetrics(string, standardFRC);
                
                return anchorY + metrics.getAscent();
            }
            
            public float getAscentLineY(String string, Font font, float anchorY)
            {
                return anchorY;
            }
            
            public float getDescentLineY(String string, Font font, float anchorY)
            {
                if ((string == null) || (font == null))
                    return anchorY;
                
                LineMetrics metrics = font.getLineMetrics(string, standardFRC);
                
                return anchorY + metrics.getAscent() + metrics.getDescent();
            }
            
            public float getLeadingLineY(String string, Font font, float anchorY)
            {
                if ((string == null) || (font == null))
                    return anchorY;
                
                LineMetrics metrics = font.getLineMetrics(string, standardFRC);
                
                return anchorY + metrics.getAscent() + metrics.getDescent()
                    + metrics.getLeading();
            }
        };
    
    
    /**
     * The <CODE>YLocator</CODE> object that corresponds to an anchor
     * on the descent line of the displayed string.
     */
    public static final YLocator DESCENTLINE =
        new YLocator() {
            public float getBaseLineY(String string, Font font, float anchorY)
            {
                if ((string == null) || (font == null))
                    return anchorY;
                
                LineMetrics metrics = font.getLineMetrics(string, standardFRC);
                
                return anchorY - metrics.getDescent();
            }
            
            public float getAscentLineY(String string, Font font, float anchorY)
            {
                if ((string == null) || (font == null))
                    return anchorY;
                
                LineMetrics metrics = font.getLineMetrics(string, standardFRC);
                
                return anchorY - metrics.getDescent() - metrics.getAscent();
            }
            
            public float getDescentLineY(String string, Font font, float anchorY)
            {
                return anchorY;
            }
            
            public float getLeadingLineY(String string, Font font, float anchorY)
            {
                if ((string == null) || (font == null))
                    return anchorY;
                
                LineMetrics metrics = font.getLineMetrics(string, standardFRC);
                
                return anchorY + metrics.getLeading();
            }
        };
    
    
    /**
     * The <CODE>YLocator</CODE> object that corresponds to an anchor
     * on the leading line of the displayed string.
     */
    public static final YLocator LEADINGLINE =
        new YLocator() {
            public float getBaseLineY(String string, Font font, float anchorY)
            {
                if ((string == null) || (font == null))
                    return anchorY;
                
                LineMetrics metrics = font.getLineMetrics(string, standardFRC);
                
                return anchorY - metrics.getLeading() - metrics.getDescent();
            }
            
            public float getAscentLineY(String string, Font font, float anchorY)
            {
                if ((string == null) || (font == null))
                    return anchorY;
                
                LineMetrics metrics = font.getLineMetrics(string, standardFRC);
                
                return anchorY - metrics.getLeading() - metrics.getDescent()
                    - metrics.getAscent();
            }
            
            public float getDescentLineY(String string, Font font, float anchorY)
            {
                if ((string == null) || (font == null))
                    return anchorY;
                
                LineMetrics metrics = font.getLineMetrics(string, standardFRC);
                
                return anchorY - metrics.getLeading();
            }
            
            public float getLeadingLineY(String string, Font font, float anchorY)
            {
                return anchorY;
            }
        };
    
    
    /**
     * The <CODE>Locator</CODE> that combines
     * <CODE>LEFT</CODE> and <CODE>BASELINE</CODE>.
     */
    public static final Locator LEFT_BASELINE =
        makeLocator(LEFT, BASELINE);
    
    
    /**
     * The <CODE>Locator</CODE> that combines
     * <CODE>RIGHT</CODE> and <CODE>BASELINE</CODE>.
     */
    public static final Locator RIGHT_BASELINE =
        makeLocator(RIGHT, BASELINE);
    
    
    /**
     * The <CODE>Locator</CODE> that combines
     * <CODE>CENTER</CODE> and <CODE>BASELINE</CODE>.
     */
    public static final Locator CENTER_BASELINE =
        makeLocator(CENTER, BASELINE);
    
    
    /**
     * The <CODE>Locator</CODE> that combines
     * <CODE>LEFT</CODE> and <CODE>ASCENTLINE</CODE>.
     */
    public static final Locator LEFT_ASCENTLINE =
        makeLocator(LEFT, ASCENTLINE);
    
    
    /**
     * The <CODE>Locator</CODE> that combines
     * <CODE>RIGHT</CODE> and <CODE>ASCENTLINE</CODE>.
     */
    public static final Locator RIGHT_ASCENTLINE =
        makeLocator(RIGHT, ASCENTLINE);
    
    
    /**
     * The <CODE>Locator</CODE> that combines
     * <CODE>CENTER</CODE> and <CODE>ASCENTLINE</CODE>.
     */
    public static final Locator CENTER_ASCENTLINE =
        makeLocator(CENTER, ASCENTLINE);
    
    
    /**
     * The <CODE>Locator</CODE> that combines
     * <CODE>LEFT</CODE> and <CODE>DESCENTLINE</CODE>.
     */
    public static final Locator LEFT_DESCENTLINE =
        makeLocator(LEFT, DESCENTLINE);
    
    
    /**
     * The <CODE>Locator</CODE> that combines
     * <CODE>RIGHT</CODE> and <CODE>DESCENTLINE</CODE>.
     */
    public static final Locator RIGHT_DESCENTLINE =
        makeLocator(RIGHT, DESCENTLINE);
    
    
    /**
     * The <CODE>Locator</CODE> that combines
     * <CODE>CENTER</CODE> and <CODE>DESCENTLINE</CODE>.
     */
    public static final Locator CENTER_DESCENTLINE =
        makeLocator(CENTER, DESCENTLINE);
    
    
    /**
     * The <CODE>Locator</CODE> that combines
     * <CODE>LEFT</CODE> and <CODE>LEADING</CODE>.
     */
    public static final Locator LEFT_LEADINGLINE =
        makeLocator(LEFT, LEADINGLINE);
    
    
    /**
     * The <CODE>Locator</CODE> that combines
     * <CODE>RIGHT</CODE> and <CODE>LEADINGLINE</CODE>.
     */
    public static final Locator RIGHT_LEADINGLINE =
        makeLocator(RIGHT, LEADINGLINE);
    
    
    /**
     * The <CODE>Locator</CODE> that combines
     * <CODE>CENTER</CODE> and <CODE>LEADINGLINE</CODE>.
     */
    public static final Locator CENTER_LEADINGLINE =
        makeLocator(CENTER, LEADINGLINE);
}
