/* grText.c -
 *
 * Contains functions for manipulating text that are not dependent
 * upon the display type.
 *
 *     *********************************************************************
 *     * Copyright (C) 1985, 1990 Regents of the University of California. *
 *     * Permission to use, copy, modify, and distribute this              *
 *     * software and its documentation for any purpose and without        *
 *     * fee is hereby granted, provided that the above copyright          *
 *     * notice appear in all copies.  The University of California        *
 *     * makes no representations about the suitability of this            *
 *     * software for any purpose.  It is provided "as is" without         *
 *     * express or implied warranty.  Export of this software outside     *
 *     * of the United States of America may require an export license.    *
 *     *********************************************************************
 */

#ifndef lint
static char rcsid[] __attribute__ ((unused)) = "$Header: /usr/cvsroot/magic-8.0/graphics/grText.c,v 1.3 2008/12/11 04:20:06 tim Exp $";
#endif  /* not lint */

#include <stdio.h>
#include <math.h>

#include "utils/magic.h"
#include "textio/textio.h"
#include "utils/geometry.h"
#include "database/database.h"
#include "windows/windows.h"
#include "graphics/graphics.h"
#include "graphics/graphicsInt.h"
#include "utils/utils.h"

/*---------------------------------------------------------
 *
 * GrFontText ---
 *
 *	This routine is a glorified version of GrPutText
 *	that draws vector outline fonts according to
 *	information loaded and stored into the font lists
 *	(see DBLoadFont() in DBlabel.c).  In addition to
 *	the properties used by GrPutText(), GrFontText()
 *	uses label scale, rotation, offset, and font.
 *
 *---------------------------------------------------------
 */

bool
GrFontText(
    char *str,		/* The text to be drawn. */
    int style,		/* Display style to use for the text */
    Point *p,		/* Point of origin */
    int font,		/* Font to use */
    int size,		/* Scale */
    int rotate,		/* Rotation (in degrees) */
    Rect *clip)		/* Clipping area */
{
    Rect nClip;
    Point pstart;

    /* Fall back on X11 text if vectored text unavailable */
    if (!grFontTextPtr)
    {
	GrPutText(str, style, p, GEO_CENTER, GR_TEXT_MEDIUM, FALSE, clip, NULL);
	return TRUE;
    }

    nClip = *clip;
    GeoClip(&nClip, &grCurClip);

    GR_CHECK_LOCK();
    if (!grDriverInformed) grInformDriver();

    if (style >= 0)
	(*grSetWMandCPtr)(GrStyleTable[style].mask, GrStyleTable[style].color);

    (*grFontTextPtr)(str, font, size, rotate, p, &nClip, grCurObscure);

    return TRUE;
}

/* The following definition defines how much text is offset from
 * its positioning point when it isn't centered.
 */

#define TEXTOFFSET 5


/*---------------------------------------------------------
 * GrPutText:
 *	This routine puts a chunk of text on the screen in the given
 *	color, size, and position.  It is clipped to a rectangle.
 *
 * Results:
 *	True if the text was able to be displayed.
 *
 * Side Effects:
 *	The text is drawn on the screen at pos relative to p, using
 *	style (text can also be erased by passing a suitable style).
 *	The rectangle 'actual' is filled in with the actual location of
 *	the text on the screen (if actual is a non-null pointer).  The
 *	text will be shrunk to a smaller font, if that will help it to
 *	fit into the clipping rectangle.
 *---------------------------------------------------------
 */

bool
GrPutText(
    char *str,	/* The text to be drawn. */
    int style,	/* The style for drawing text; if -1 then
			 * the caller has already set the style
			 * and we don't have to.
			 */
    Point *p,	/* The point to align with */
    int pos,	/* The alignment desired (GR_NORTH,
			 * GR_NORTHEAST, etc.)
			 */
    int size,	/* The desired size of the text
			 * (such as GR_TEXT_MEDIUM).
			 */
    bool adjust,	/* TRUE means adjust the text (either by
			 * sliding it around or using a smaller font)
			 * if that is necessary to make it fit into
			 * the clipping rectangle.  FALSE means
			 * display the text exactly as instructed,
			 * clipping it if it doesn't fit.
			 */
    Rect *clip,	/* A clipping rectangle for the text */
    Rect *actual)	/* To be filled in with the location of the
			 * text.
			 */
{
    Rect posR;
    Point drawPoint;
    int xpos, ypos, hangBelow;
    Rect nClip;

    nClip = *clip;
    GeoClip(&nClip, &grCurClip);

    GR_CHECK_LOCK();
    if (!grDriverInformed) grInformDriver();

    if (actual != (Rect *) NULL)
    {
	actual->r_xbot = actual->r_ybot = 0;
	actual->r_xtop = actual->r_ytop = 0;
    }

    /* The following loop sees if the text will fit in the clipping
     * area.  If not, and shrinking is allowed, we try again and
     * again with smaller sizes.
     */

    while (TRUE)
    {
	/* what portion of the screen is taken up by the text? */
	GrTextSize(str, size, &posR);
	hangBelow = -posR.r_ybot;

	/* figure out where the text will go, including a border on 1 side  */

	switch (pos)	/* horizontal centering */
	{
	    case GEO_NORTHWEST:
	    case GEO_WEST:
	    case GEO_SOUTHWEST:
		xpos = p->p_x - TEXTOFFSET - posR.r_xtop;
		break;
	    case GEO_NORTH:
	    case GEO_CENTER:
	    case GEO_SOUTH:
		xpos = p->p_x - posR.r_xtop/2;
		break;
	    case GEO_NORTHEAST:
	    case GEO_EAST:
	    case GEO_SOUTHEAST:
		xpos = p->p_x + TEXTOFFSET;
		break;
	    default:
		TxError("Illegal position (%d) for text (internal error)\n",
		    pos);
		return FALSE;
		break;
	}
	switch (pos)	/* vertical centering */
	{
	    case GEO_NORTH:
	    case GEO_NORTHEAST:
	    case GEO_NORTHWEST:
		ypos = p->p_y + TEXTOFFSET;
		break;
	    case GEO_CENTER:
	    case GEO_WEST:
	    case GEO_EAST:
		ypos = p->p_y - (posR.r_ytop / 2);
		break;
	    case GEO_SOUTH:
	    case GEO_SOUTHEAST:
	    case GEO_SOUTHWEST:
		ypos = p->p_y - posR.r_ytop - TEXTOFFSET;
		break;
	}

	/* move area to screen coordinates */
	posR.r_xbot += xpos;
	posR.r_xtop += xpos;
	posR.r_ybot += ypos;
	posR.r_ytop += ypos;

	/* will that area fit within the clipping rectangle? */
	if ( (posR.r_xtop <= nClip.r_xtop) && (posR.r_xbot >= nClip.r_xbot) &&
	    (posR.r_ytop <= nClip.r_ytop) && (posR.r_ybot >= nClip.r_ybot) )
	{
	    /* it fits! */
	    break;
	}

	/* it doesn't fit, will sliding it be enough? */
	if (adjust)
	{
	    if ( ((nClip.r_xtop-nClip.r_xbot) >= (posR.r_xtop - posR.r_xbot)) &&
		((nClip.r_ytop - nClip.r_ybot) >= (posR.r_ytop - posR.r_ybot)) )
	    {
		/* it will fit */
		break;
	    }

	}

	/* Won't fit even with sliding, so shrink if possible. */
	if (adjust && (size > 0) )
	{
	    /* maybe shrinking it will help */
	    size -= 1;
	}
	else break;

    } /* while */


    /* ASSERTION: We've shrunk things to the proper size at this point.  */

    /* Slide the text, if that is allowable and needed.  We'll only
     * slide the text if there's available space on one side and
     * insufficient space on the other.
     */

    if (adjust)
    {
	int top, bottom, left, right;	/* Space needed on each side. */
	int slide;

	right = posR.r_xtop - nClip.r_xtop;
	left = nClip.r_xbot - posR.r_xbot;
	top = posR.r_ytop - nClip.r_ytop;
	bottom = nClip.r_ybot - posR.r_ybot;

	slide = 0;
	if (right > 0)
	{
	    if (left < 0) slide = MAX(-right, left);
	}
	else if (left > 0) slide = MIN(left, -right);
	posR.r_xbot += slide;
	posR.r_xtop += slide;

	slide = 0;
	if (top > 0)
	{
	    if (bottom < 0) slide = MAX(-top, bottom);
	}
	else if (bottom > 0) slide = MIN(bottom, -top);
	posR.r_ybot += slide;
	posR.r_ytop += slide;
    }

    /* ASSERTION: By now the text is positioned properly. */

    /* do all clipping within the grPutTextPtr routine */

    (*grSetCharSizePtr)(size);
    if (style >= 0)
	(*grSetWMandCPtr)(GrStyleTable[style].mask, GrStyleTable[style].color);

    drawPoint.p_x = posR.r_xbot;
    drawPoint.p_y = posR.r_ybot + hangBelow;
    (*grPutTextPtr)(str, &drawPoint, &nClip, grCurObscure);

    if (actual != (Rect *) NULL)
	*actual = posR;
    return TRUE;
}

/*
 * ----------------------------------------------------------------------------
 *
 * GrLabelSize --
 *
 * 	Determines the total size, in pixels, of a label.
 *	Used to compute how much to redisplay when a label changes.
 *
 * Results:
 *	None.
 *
 * Side effects:
 *	The parameter "area" is modified to contain the size, in pixels,
 *	of the given label, relative to its positioning point.  This
 *	size includes just the text, and not any marker (e.g. cross
 * 	displayed at the label position).
 *
 * ----------------------------------------------------------------------------
 */

void
GrLabelSize(
    char *text,				/* Text of the label. */
    int pos,				/* Position of the label relative
					 * to its positioning point.
					 */
    int size,				/* Text size. */
    Rect *area)				/* To be filled in with label size. */
{
    int xoffset, yoffset;		/* Offsets due to label position. */

    GrTextSize(text, size, area);

    /* Now we now the text's size... but if it's positioned to one
     * side of the point, we have to offset it.
     */

    switch (pos)			/* Get x offset first. */
    {
	case GEO_NORTHWEST:
	case GEO_WEST:
	case GEO_SOUTHWEST:
	    xoffset = - TEXTOFFSET - area->r_xtop;
	    break;
	case GEO_NORTH:
	case GEO_CENTER:
	case GEO_SOUTH:
	    xoffset =  - area->r_xtop/2;
	    break;
	case GEO_NORTHEAST:
	case GEO_EAST:
	case GEO_SOUTHEAST:
	    xoffset = TEXTOFFSET;
	    break;
    }
    switch (pos)			/* Now get y offset. */
    {
	case GEO_NORTH:
	case GEO_NORTHEAST:
	case GEO_NORTHWEST:
	    yoffset = TEXTOFFSET;
	    break;
	case GEO_CENTER:
	case GEO_WEST:
	case GEO_EAST:
	    yoffset = - area->r_ytop/2;
	    break;
	case GEO_SOUTH:
	case GEO_SOUTHEAST:
	case GEO_SOUTHWEST:
	    yoffset = - area->r_ytop - TEXTOFFSET;
	    break;
    }

    area->r_xbot += xoffset;
    area->r_xtop += xoffset;
    area->r_ybot += yoffset;
    area->r_ytop += yoffset;
}
