/* 
 * Motif
 *
 * Copyright (c) 1987-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they 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 these librararies and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
*/ 
/* 
 * Motif Release 1.2
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

 
#ifdef REV_INFO
#ifndef lint
static char rcsid[] = "$XConsortium: WmGraphics.c /main/4 1995/11/01 11:38:53 rswiston $"
#endif
#endif

/*
 * Included Files:
 */

#include "WmGlobal.h"

#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/StringDefs.h>
#include <X11/Xutil.h>
#include <X11/Intrinsic.h>
#include <X11/X.h>
#include <Xm/Xm.h>


#define RLIST_EXTENSION_SIZE	10

#ifndef MIN
#define MAX(x,y) ((x)>(y)?(x):(y))
#endif

/*
 * include extern functions
 */
#include "WmGraphics.h"
#include "WmError.h"



/*
 * Global Variables:
 */


/*
 * Macros:
 */

/* test if > 0 and return 1 if true, 0 if false. */
#define GE1(x) ((x)>0?1:0)



/*************************************<->*************************************
 *
 *   Procedure:	BevelRectangle (prTop, prBot, x, y, 
 *                      width, height, top_wid, right_wid, bot_wid, left_wid)
 *
 *  Description:
 *  -----------
 *  Generate data for top- and bottom-shadow bevels on a box.
 *
 *  Inputs:
 *  ------
 *  prTop	- ptr to top shadow RList
 *  prBot	- ptr to bottom shadow RList
 *  x,y		- position of rectangle to bevel
 *  width 	- (outside) width of rectangle
 *  height 	- (outside) height of rectangle
 *  top_wid	- width of beveling on top side of rectangle
 *  right_wid	- width of beveling on right side of rectangle
 *  bot_wid	- width of beveling on bottom side of rectangle
 *  left_wid	- width of beveling on left side of rectangle
 * 
 *  Outputs:
 *  -------
 *  prTop	- top shadows for this rectangle added to list
 *  prBot	- bottom shadows for this rectangle added to list
 *  
 *
 *  Comments:
 *  --------
 *
 *************************************<->***********************************/

void BevelRectangle (RList *prTop, RList *prBot, int x, int y, unsigned int width, unsigned int height, unsigned int top_wid, unsigned int right_wid, unsigned int bot_wid, unsigned int left_wid)
{
    XRectangle *prect;		/* pointer to "current" rectangle */
    register int count;		/* counter used for beveling operation */
    int join1, join2;		/* used to compute "good" bevel joints */
    int x1, y1, len;		/* used to compute bevel parameters */
    int *piTop, *piBot;


    /* build the rectangles to implement the beveling on each side */

    /* top side */

    if (((prTop->used + (top_wid + left_wid)) > prTop->allocated) &&
	(!ExtendRList (prTop, MAX (top_wid+left_wid, RLIST_EXTENSION_SIZE))))
    {
	return;		/* not enough memory */
    }

    piTop = &(prTop->used);
    prect = &(prTop->prect[*piTop]);

    join1 = left_wid;
    join2 = right_wid;
    x1 = x;
    y1 = y;
    len = width;
    for (count=top_wid; count>0; count--, prect++, (*piTop)++) 
    {
	prect->x = x1;
	prect->y = y1;
	prect->width = len;
	prect->height = 1;
	x1 += GE1(--join1);
	y1 += 1;
	len -= GE1(join1) + GE1(--join2);
    }

    /* left side */

    join1 = top_wid;
    join2 = bot_wid;
    x1 = x;
    y1 = y+GE1(join1);
    len = height-GE1(join1);
    for (count=left_wid; count >0; count--, prect++, (*piTop)++) 
    {	
	prect->x = x1;
	prect->y = y1;
	prect->width = 1;
	prect->height = len;
	x1 += 1;
	y1 += GE1(--join1);
	len -= GE1(join1) + GE1(--join2);
    }


    /* bottom side */

    if (((prBot->used + (bot_wid + right_wid)) > prBot->allocated) &&
	(!ExtendRList(prBot, MAX (bot_wid+right_wid, RLIST_EXTENSION_SIZE))))
    {
	return;
    }

    piBot = &(prBot->used);
    prect = &(prBot->prect[*piBot]);

    join1 = left_wid;
    join2 = right_wid;
    x1 = x+GE1(join1);
    y1 = y+height-1;
    len = width-GE1(join1);
    /* fudge fat bottom shadow to overwrite corner of skinny left shadow */
    if (GE1(join1) && (bot_wid > left_wid)) {
	len++;
	x1--;
	join1++;
    }
    for (count=bot_wid; count >0; count--, prect++, (*piBot)++) 
    {
	prect->x = x1;
	prect->y = y1;
	prect->width = len;
	prect->height = 1;
	x1 += GE1(--join1);
	y1 -= 1;
	len -= GE1(join1) + GE1(--join2);
    }

    /* right side */

    join1 = top_wid;
    join2 = bot_wid;
    x1 = x+width-1;
    y1 = y+GE1(join1);
    len = height - GE1(join1) - GE1(join2);
    /* fudge fat right shadow to overwrite corner of skinny top shadow */
    if (GE1(join1) && (right_wid > top_wid)) {
	len++;
	y1--;
	join1++;
    }
    for (count=right_wid; count >0; count--, prect++, (*piBot)++) 
    {
	prect->x = x1;
	prect->y = y1;
	prect->width = 1;
	prect->height = len;
	x1 -= 1;
	y1 += GE1(--join1);
	len -= GE1(join1) + GE1(--join2);
    }

} /* END OF FUNCTION BevelRectangle */




/*************************************<->*************************************
 *
 *   Procedure:	BevelDepressedRectangle (prTop, prBot, x, y, 
 *                      width, height, top_wid, right_wid, bot_wid, left_wid
 *                      in_wid)
 *
 *  Description:
 *  -----------
 *  Generate data for top- and bottom-shadow bevels on a rectangle with
 *  the center part depressed.
 *
 *  Inputs:
 *  ------
 *  prTop	- ptr to top shadow RList
 *  prBot	- ptr to bottom shadow RList
 *  x,y		- position of rectangle to bevel
 *  width 	- (outside) width of rectangle
 *  height 	- (outside) height of rectangle
 *  top_wid	- width of beveling on top side of rectangle
 *  right_wid	- width of beveling on right side of rectangle
 *  bot_wid	- width of beveling on bottom side of rectangle
 *  left_wid	- width of beveling on left side of rectangle
 *  in_wid	- width of depressed beveling inside of rectangle
 * 
 *  Outputs:
 *  -------
 *  prTop	- top shadows for this rectangle added to list
 *  prBot	- bottom shadows for this rectangle added to list
 *  
 *
 *  Comments:
 *  --------
 *
 *************************************<->***********************************/

void BevelDepressedRectangle (RList *prTop, RList *prBot, int x, int y, unsigned int width, unsigned int height, unsigned int top_wid, unsigned int right_wid, unsigned int bot_wid, unsigned int left_wid, unsigned int in_wid)
{
    XRectangle *prect;		/* pointer to "current" rectangle */
    register int count;		/* counter used for beveling operation */
    int join1, join2;		/* used to compute "good" bevel joints */
    int x1, y1, len;		/* used to compute bevel parameters */
    int *piTop, *piBot;


    /* 
     *  Build the rectangles to implement the beveling on each side 
     *  First, guarantee that there is enough memory.
     */


    if (((prTop->used + (top_wid + left_wid)) > prTop->allocated) &&
	(!ExtendRList (prTop, MAX (top_wid+left_wid, RLIST_EXTENSION_SIZE))))
    {
	return;		/* not enough memory */
    }

    if (((prBot->used + (bot_wid + right_wid)) > prBot->allocated) &&
	(!ExtendRList(prBot, MAX (bot_wid+right_wid, RLIST_EXTENSION_SIZE))))
    {
	return;		/* not enought memory */
    }



    /* top side (normal beveling) */

    piTop = &(prTop->used);
    prect = &(prTop->prect[*piTop]);

    join1 = left_wid;
    join2 = right_wid;
    x1 = x;
    y1 = y;
    len = width;
    for (count=top_wid - in_wid; count>0; count--, prect++, (*piTop)++) 
    {
	prect->x = x1;
	prect->y = y1;
	prect->width = len;
	prect->height = 1;
	x1 += GE1(--join1);
	y1 += 1;
	len -= GE1(join1) + GE1(--join2);
    }

    /* top side (inverted beveling) */

    piBot = &(prBot->used);
    prect = &(prBot->prect[*piBot]);

    for (count=in_wid; count>0; count--, prect++, (*piBot)++) 
    {
	prect->x = x1;
	prect->y = y1;
	prect->width = len;
	prect->height = 1;
	x1 += GE1(--join1);
	y1 += 1;
	len -= GE1(join1) + GE1(--join2);
    }



    /* left side (normal beveling) */

    piTop = &(prTop->used);
    prect = &(prTop->prect[*piTop]);

    join1 = top_wid;
    join2 = bot_wid;
    x1 = x;
    y1 = y+GE1(join1);
    len = height-GE1(join1);
    for (count=left_wid-in_wid; count >0; count--, prect++, (*piTop)++) 
    {	
	prect->x = x1;
	prect->y = y1;
	prect->width = 1;
	prect->height = len;
	x1 += 1;
	y1 += GE1(--join1);
	len -= GE1(join1) + GE1(--join2);
    }

    /* left side (inverted beveling) */

    piBot = &(prBot->used);
    prect = &(prBot->prect[*piBot]);

    for (count=in_wid; count >0; count--, prect++, (*piBot)++) 
    {	
	prect->x = x1;
	prect->y = y1;
	prect->width = 1;
	prect->height = len;
	x1 += 1;
	y1 += GE1(--join1);
	len -= GE1(join1) + GE1(--join2);
    }



    /* bottom side (normal beveling) */

    piBot = &(prBot->used);
    prect = &(prBot->prect[*piBot]);

    join1 = left_wid;
    join2 = right_wid;
    x1 = x+GE1(join1);
    y1 = y+height-1;
    len = width-GE1(join1);
    /* fudge fat bottom shadow to overwrite corner of skinny left shadow */
    if (GE1(join1) && (bot_wid > left_wid)) {
	len++;
	x1--;
	join1++;
    }
    for (count=bot_wid-in_wid; count >0; count--, prect++, (*piBot)++) 
    {
	prect->x = x1;
	prect->y = y1;
	prect->width = len;
	prect->height = 1;
	x1 += GE1(--join1);
	y1 -= 1;
	len -= GE1(join1) + GE1(--join2);
    }

    /* bottom side (inverted beveling) */

    piTop = &(prTop->used);
    prect = &(prTop->prect[*piTop]);

    for (count=in_wid; count >0; count--, prect++, (*piTop)++) 
    {
	prect->x = x1;
	prect->y = y1;
	prect->width = len;
	prect->height = 1;
	x1 += GE1(--join1);
	y1 -= 1;
	len -= GE1(join1) + GE1(--join2);
    }



    /* right side (normal beveling) */

    piBot = &(prBot->used);
    prect = &(prBot->prect[*piBot]);

    join1 = top_wid;
    join2 = bot_wid;
    x1 = x+width-1;
    y1 = y+GE1(join1);
    len = height - GE1(join1) - GE1(join2);
    /* fudge fat right shadow to overwrite corner of skinny top shadow */
    if (GE1(join1) && (right_wid > top_wid)) {
	len++;
	y1--;
	join1++;
    }
    for (count=right_wid-in_wid; count >0; count--, prect++, (*piBot)++) 
    {
	prect->x = x1;
	prect->y = y1;
	prect->width = 1;
	prect->height = len;
	x1 -= 1;
	y1 += GE1(--join1);
	len -= GE1(join1) + GE1(--join2);
    }

    /* right side (inverted beveling) */

    piTop = &(prTop->used);
    prect = &(prTop->prect[*piTop]);

    for (count=in_wid; count >0; count--, prect++, (*piTop)++) 
    {
	prect->x = x1;
	prect->y = y1;
	prect->width = 1;
	prect->height = len;
	x1 -= 1;
	y1 += GE1(--join1);
	len -= GE1(join1) + GE1(--join2);
    }

} /* END OF FUNCTION BevelDepressedRectangle */




/*************************************<->*************************************
 *
 *   Procedure:	StretcherCorner (prTop, prBot, x, y, cnum, 
 *                               swidth,  cwidth, cheight);
 *
 *  Description:
 *  -----------
 *  Generate data to draw a corner of the stretcher border.
 *
 *  Inputs:
 *  ------
 *  prTop	- ptr to top shadow RList
 *  prBot	- ptr to bottom shadow RList
 *  x,y		- position of rectangle enclosing the cornern
 *  cnum 	- corner number; which corner to draw
 *                ASSUMES only NW, NE, SE, SW for mwm ()
 *  swidth 	- width (thickness) of border (includes bevels)
 *  cwidth 	- corner width from corner to end of horizontal run
 *  cheight 	- corner height from corner to end of vertical run
 * 
 *  Outputs:
 *  -------
 *  prTop	- array filled in for top shadows 
 *  prBot	- array filledin for bottom shadows
 *
 *  Comments:
 *  --------
 *  o Uses only 1 pixel bevels. Beveling is hard coded.
 *  o XFillRectangles assumed as an optimization to take
 *    advantage of the block mover hardware.
 *
 *************************************<->***********************************/

void StretcherCorner (RList *prTop, RList *prBot, int x, int y, int cnum, unsigned int swidth, unsigned int cwidth, unsigned int cheight)
{
    XRectangle *prect;		/* pointer to "current" rectangle */
    int *piTop, *piBot;

    switch (cnum) {

	case STRETCH_NORTH_WEST:
		if (((prTop->used + 4) > prTop->allocated) &&
		    (!ExtendRList (prTop, (unsigned int) RLIST_EXTENSION_SIZE)))
		{
		    return;
		}

		piTop = &(prTop->used);
		prect = &(prTop->prect[*piTop]);

		prect->x = x;			/* top (row 1) */
		prect->y = y;
		prect->width = cwidth;
		prect->height = 1;
		prect++;
		(*piTop)++;

		prect->x = x+1;			/* top (row 2) */
		prect->y = y+1;
		prect->width = cwidth-2;
		prect->height = 1;
		prect++;
		(*piTop)++;

		prect->x = x;			/* left (col 1) */
		prect->y = y+1;
		prect->width = 1;
		prect->height = cheight-1;
		prect++;
		(*piTop)++;

		prect->x = x+1;			/* left (col 2) */
		prect->y = y+2;
		prect->width = 1;
		prect->height = cheight-3;
		(*piTop)++;

		if (((prBot->used + 4) > prBot->allocated) &&
		    (!ExtendRList (prBot, (unsigned int) RLIST_EXTENSION_SIZE)))
		{
		    return;
		}

		piBot = &(prBot->used);
		prect = &(prBot->prect[*piBot]); /* bottom shadow parts */


		prect->x = x+1;			/* bottom end */
		prect->y = y+cheight-1;
		prect->width = swidth-1;
		prect->height = 1;
		prect++;
		(*piBot)++;

		if (wmGD.frameStyle == WmRECESSED)
		{
		    prect->x = x+swidth-1;		/* right inside */
		    prect->y = y+swidth-1;
		    prect->width = 1;
		    prect->height = cheight-swidth;
		    prect++;
		    (*piBot)++;

		    prect->x = x+swidth;		/* bottom inside */
		    prect->y = y+swidth-1;
		    prect->width = cwidth-swidth;
		    prect->height = 1;
		    prect++;
		    (*piBot)++;
		}

		prect->x = x+cwidth-1;		/* right end */
		prect->y = y+1;
		prect->width = 1;
		prect->height = swidth-1-((wmGD.frameStyle == WmSLAB)? 0 : 1);
		(*piBot)++;

		break;

	case STRETCH_NORTH_EAST:
		if (((prTop->used + 4) > prTop->allocated) &&
		    (!ExtendRList (prTop, (unsigned int) RLIST_EXTENSION_SIZE)))
		{
		    return;
		}

		piTop = &(prTop->used);
		prect = &(prTop->prect[*piTop]);

		prect->x = x;			/* top (row 1) */
		prect->y = y;
		prect->width = cwidth;
		prect->height = 1;
		prect++;
		(*piTop)++;

		prect->x = x+1;			/* top (row 2) */
		prect->y = y+1;
		prect->width = cwidth-2;
		prect->height = 1;
		prect++;
		(*piTop)++;

		prect->x = x;			/* left end */
		prect->y = y+1;
		prect->width = 1;
		prect->height = swidth-1;
		prect++;
		(*piTop)++;

		if (wmGD.frameStyle == WmRECESSED)
		{
		    prect->x = x+cwidth-swidth;	/* left inside (col 1) */
		    prect->y = y+swidth;
		    prect->width = 1;
		    prect->height = cheight-swidth;
		    (*piTop)++;
		}

		if (((prBot->used + 4) > prBot->allocated) &&
		    (!ExtendRList (prBot, (unsigned int) RLIST_EXTENSION_SIZE)))
		{
		    return;
		}

		piBot = &(prBot->used);
		prect = &(prBot->prect[*piBot]); /* bottom shadow parts */


		/* bottom end */
		prect->x = x+cwidth-swidth+((wmGD.frameStyle == WmSLAB)? 0 : 1);
		prect->y = y+cheight-1;
		prect->width = swidth-((wmGD.frameStyle == WmSLAB)? 0 : 1);
		prect->height = 1;
		prect++;
		(*piBot)++;

		prect->x = x+cwidth-1;		/* right (col 2) */
		prect->y = y+1;
		prect->width = 1;
		prect->height = cheight-2;
		prect++;
		(*piBot)++;

		prect->x = x+cwidth-2;		/* right (col 1) */
		prect->y = y+2;
		prect->width = 1;
		prect->height = cheight-3;
		prect++;
		(*piBot)++;

		if (wmGD.frameStyle == WmRECESSED)
		{
		    prect->x = x+1;		/* bottom inside (row 2) */
		    prect->y = y+swidth-1;
		    prect->width = cwidth-swidth;
		    prect->height = 1;
		    (*piBot)++;
		}

		break;

	case STRETCH_SOUTH_EAST:
		if (((prTop->used + 4) > prTop->allocated) &&
		    (!ExtendRList (prTop, (unsigned int) RLIST_EXTENSION_SIZE)))
		{
		    return;
		}

		piTop = &(prTop->used);
		prect = &(prTop->prect[*piTop]);

		if (wmGD.frameStyle == WmRECESSED)
		{
		    prect->x = x;			/* top inside */
		    prect->y = y+cheight-swidth;
		    prect->width = cwidth-swidth+1;
		    prect->height = 1;
		    prect++;
		    (*piTop)++;

		    prect->x = x+cwidth-swidth;	/* left inside */
		    prect->y = y;
		    prect->width = 1;
		    prect->height = cheight-swidth;
		    prect++;
		    (*piTop)++;
		}

		/* top end */
		prect->x = x+cwidth-swidth+
				((wmGD.frameStyle == WmSLAB)? 0 : 1);
		prect->y = y;
		prect->width = swidth-2+((wmGD.frameStyle == WmSLAB)? 1 : 0);;
		prect->height = 1;
		prect++;
		(*piTop)++;

		prect->x = x;			/* left end */
		prect->y = y+cheight-swidth+
				((wmGD.frameStyle == WmSLAB)? 0 : 1);
		prect->width = 1;
		prect->height = swidth-2+((wmGD.frameStyle == WmSLAB)? 1 : 0);
		(*piTop)++;

		if (((prBot->used + 4) > prBot->allocated) &&
		    (!ExtendRList (prBot, (unsigned int) RLIST_EXTENSION_SIZE)))
		{
		    return;
		}

		piBot = &(prBot->used);
		prect = &(prBot->prect[*piBot]); /* bottom shadow parts */


		prect->x = x+1;			/* bottom - row 1 */
		prect->y = y+cheight-2;
		prect->width = cwidth-1;
		prect->height = 1;
		prect++;
		(*piBot)++;

		prect->x = x;			/* bottom - row 2 */
		prect->y = y+cheight-1;
		prect->width = cwidth;
		prect->height = 1;
		prect++;
		(*piBot)++;

		prect->x = x+cwidth-2;		/* right  - column 1 */
		prect->y = y+1;
		prect->width = 1;
		prect->height = cheight-3;
		prect++;
		(*piBot)++;

		prect->x = x+cwidth-1;		/* right  - column 2 */
		prect->y = y;
		prect->width = 1;
		prect->height = cheight-2;
		(*piBot)++;

		break;

	case STRETCH_SOUTH_WEST:
		if (((prTop->used + 4) > prTop->allocated) &&
		    (!ExtendRList (prTop, (unsigned int) RLIST_EXTENSION_SIZE)))
		{
		    return;
		}

		piTop = &(prTop->used);
		prect = &(prTop->prect[*piTop]);

		prect->x = x;			/* top end */
		prect->y = y;
		prect->width = swidth;
		prect->height = 1;
		prect++;
		(*piTop)++;

		prect->x = x;			/* left (col 1) */
		prect->y = y+1;
		prect->width = 1;
		prect->height = cheight-1;
		prect++;
		(*piTop)++;

		prect->x = x+1;			/* left (col 2) */
		prect->y = y+1;
		prect->width = 1;
		prect->height = cheight-2;
		prect++;
		(*piTop)++;

		if (wmGD.frameStyle == WmRECESSED)
		{
		    prect->x = x+swidth;		/* top inside (row 2) */
		    prect->y = y+cheight-swidth;
		    prect->width = cwidth-swidth;
		    prect->height = 1;
		    (*piTop)++;
		}

		if (((prBot->used + 4) > prBot->allocated) &&
		    (!ExtendRList (prBot, (unsigned int) RLIST_EXTENSION_SIZE)))
		{
		    return;
		}

		piBot = &(prBot->used);
		prect = &(prBot->prect[*piBot]); /* bottom shadow parts */


		if (wmGD.frameStyle == WmRECESSED)
		{
		    prect->x = x+swidth-1;	/* right inside (col 2) */
		    prect->y = y+1;
		    prect->width = 1;
		    prect->height = cheight-swidth;
		    prect++;
		    (*piBot)++;
		}

		prect->x = x+cwidth-1;		/* right end */
		prect->y = y+cheight-swidth+
				((wmGD.frameStyle == WmSLAB)? 0 : 1);
		prect->width = 1;
		prect->height = swidth-((wmGD.frameStyle == WmSLAB)? 0 : 1);
		prect++;
		(*piBot)++;

		prect->x = x+2;			/* bottom (row 1) */
		prect->y = y+cheight-2;
		prect->width = cwidth-3;
		prect->height = 1;
		prect++;
		(*piBot)++;

		prect->x = x+1;			/* bottom (row 2) */
		prect->y = y+cheight-1;
		prect->width = cwidth-2;
		prect->height = 1;
		(*piBot)++;

		break;
    }
} /* END OF FUNCTION StretcherCorner */



/*************************************<->*************************************
 *
 *  DrawStringInBox (dpy, win, gc, pbox, str)
 *
 *
 *  Description:
 *  -----------
 *  Draws a null-terminated string inside the specified box (rectangle)
 *
 *
 *  Inputs:
 *  ------
 *  dpy		- ptr to Display
 *  win		- an X Window
 *  gc 		- graphics context to use
 *  pfs		- pointer to XFontStruct for the font in "gc"
 *  pbox	- ptr to XRectangle that encloses text
 *  str		- String to write
 * 
 *  Outputs:
 *  -------
 *  none
 *
 *  Comments:
 *  --------
 *  o Assumes 8-bit text for now.
 *  o Algorithm:  
 *			get length of String
 *			if String is short than box width then
 *			    draw string centered in box
 *			otherwise
 *			    draw string left justified and clipped to box
 *  o The clip_x_origin, clip_y_origin, and clip_mask are reset to None
 *    upon exit.
 *  o Due to bugs and / or misunderstanding, I gave up on trying to 
 *    extract the XFontStruct from the GC. I just made it a separate 
 *    parameter.
 *			
 *************************************<->***********************************/
void DrawStringInBox (Display *dpy, Window win, GC gc, XFontStruct *pfs, XRectangle *pbox, String str)
{
    XGCValues gcv;
    int textWidth;
    int xCenter;
    XRectangle clipBox;

    /* compute text position */
    textWidth = XTextWidth(pfs, str, strlen(str));

    if (textWidth < (int) pbox->width) {	/* center text if there's room */
	xCenter = (int) pbox->x + ((int) pbox->width - textWidth) / 2 ;
	WmDrawString(dpy, win, gc, xCenter, (pbox->y + pfs->ascent), 
		    str, strlen(str));
    }
    else {				/* left justify & clip text */
	clipBox.x = 0;			/* set up clip rectangle */
	clipBox.y = 0;
	clipBox.width = pbox->width;
	clipBox.height = pbox->height;

	XSetClipRectangles (dpy, gc, pbox->x, pbox->y,	/* put into gc */
			    &clipBox, 1, Unsorted);

	WmDrawString(dpy, win, gc, pbox->x, (pbox->y + pfs->ascent), 
		    str, strlen(str));

	gcv.clip_x_origin = 0;		/* erase clip_mask from gc */
	gcv.clip_y_origin = 0;
	gcv.clip_mask = None;
	XChangeGC (dpy, gc, 
		   GCClipXOrigin | GCClipYOrigin | GCClipMask, &gcv);
    }
} /* END OF FUNCTION DrawStringInBox */




/*************************************<->*************************************
 *
 *  ExtendRList (prl, amt)
 *
 *
 *  Description:
 *  -----------
 *  Extends the size of the RList
 *
 *
 *  Inputs:
 *  ------
 *  prl		- ptr to Display
 *  amt		- how much to extend it by
 * 
 *  Outputs:
 *  -------
 *  Returns True if succeeded, false otherwise.
 *
 *  Comments:
 *  --------
 *			
 *************************************<->***********************************/
Boolean ExtendRList (RList *prl, unsigned int amt)
{
    unsigned int total, count;
    XRectangle *pNewRect;
    Boolean rval;


    total = prl->allocated + amt;
    if ( (pNewRect = (XRectangle *) XtMalloc (total * sizeof(XRectangle))) 
	 == NULL)
    {
	Warning (((char *)GETMESSAGE(28, 1, "Insufficient memory for graphics data")));
	rval = False;
    }
    else 
    {
	prl->allocated = total;
	rval = True;

	if (prl->used != 0) {		/* copy from old structure */
	    count = prl->used * sizeof(XRectangle);
	    (void) memcpy ((void *)pNewRect, (void *)prl->prect, count);
	    if (prl->prect != NULL)
		XtFree ((char *)prl->prect);
	    prl->prect = pNewRect;
	}
    }
    return (rval);
} /* END OF FUNCTION ExtendRList */



/*************************************<->*************************************
 *
 *  AllocateRList (amt)
 *
 *
 *  Description:
 *  -----------
 *  Allocates an RList of size "amt"
 *
 *
 *  Inputs:
 *  ------
 *  amt		- number of XRectangles to allocate in list
 * 
 *  Outputs:
 *  -------
 *  Returns ptr to new RList structure if success, returns NULL ptr otherwise
 *
 *  Comments:
 *  --------
 *			
 *************************************<->***********************************/
RList *AllocateRList (amt)

    unsigned int amt;
{
    RList *prl;


    if ((prl = (RList *) XtMalloc (sizeof (RList))) != NULL) 
    {
	if ( (prl->prect = (XRectangle *) XtMalloc (amt * sizeof(XRectangle))) 
	     == NULL)
	{
	    XtFree ((char *)prl);
	    prl = NULL;
	}
	else 
	{
	    prl->allocated = amt;
	    prl->used = 0;

	}
    }

    return (prl);
} /* END OF FUNCTION AllocateRList */



/*************************************<->*************************************
 *
 *  FreeRList (prl)
 *
 *
 *  Description:
 *  -----------
 *  Frees an RList
 *
 *
 *  Inputs:
 *  ------
 *  prl		- ptr to RList to free
 * 
 *  Outputs:
 *  -------
 *
 *  Comments:
 *  --------
 *			
 *************************************<->***********************************/
void FreeRList (RList *prl)
{
    if (prl) 
    {
	if (prl->prect) 
	    XtFree ((char *)prl->prect);

	XtFree ((char *)prl);
    }
}/* END OF FUNCTION FreeRList */


/*************************************<->*************************************
 *
 *  WmDrawString
 *
 *
 *  Description:
 *  -----------
 *  Draws a string
 *
 *
 *  Inputs:
 *  ------
 *  (same parameters used by XDrawString and XDrawImageString)
 * 
 *  Outputs:
 *  -------
 *
 *  Comments:
 *  --------
 *  o If wmGD.cleanText is True, then the text is drawn using 
 *    XDrawImageString. This provides some clean space around the text
 *    if the background area is stippled -- especially useful on 
 *    B/W displays.
 *			
 *************************************<->***********************************/
void WmDrawString (Display *dpy, Drawable d, GC gc, int x, int y, char *string, unsigned int length)
{
    if (ACTIVE_PSD->cleanText)
    {
	XDrawImageString(dpy, d, gc, x, y, string, length);
    }
    else
    {
	XDrawString(dpy, d, gc, x, y, string, length);
    }

}/* END OF FUNCTION WmDrawString */



/*************************************<->*************************************
 *
 *  WmXmDrawString
 *
 *
 *  Description:
 *  -----------
 *  Draws a string
 *
 *
 *  Inputs:
 *  ------
 *  (subset of parameters used by XmStringDraw and XmStringDrawImage)
 * 
 *  Outputs:
 *  -------
 *
 *  Comments:
 *  --------
 *  o If wmGD.cleanText is True, then the text is drawn using 
 *    XmStringDrawImage. This provides some clean space around the text
 *    if the background area is stippled -- especially useful on 
 *    B/W displays.
 *			
 *************************************<->***********************************/
#ifdef WSM
void WmDrawXmString (Display *dpy, Window w, XmFontList xmfontlist, 
		     XmString xmstring, GC gc, Position x, Position y, 
		     Dimension width,  XRectangle *pbox, Boolean bCenter)
#else /* WSM */
void WmDrawXmString (Display *dpy, Window w, XmFontList xmfontlist, 
		     XmString xmstring, GC gc, Position x, Position y, 
		     Dimension width,  XRectangle *pbox)
#endif /* WSM */
{
    Dimension textWidth;
#ifdef WSM
    int alignment;
#else /* WSM */
    int alignment = XmALIGNMENT_BEGINNING;
#endif /* WSM */
    

    textWidth = XmStringWidth(xmfontlist, xmstring);

#ifdef WSM
    alignment = bCenter ? XmALIGNMENT_CENTER : XmALIGNMENT_BEGINNING;

    if (textWidth >= pbox->width)  /* can't center text if no room */
    {                              /* left justify & clip text */
	alignment = XmALIGNMENT_BEGINNING;
    }
#else /* WSM */
    if (textWidth < pbox->width) {      /* center text if there's room */
	alignment = XmALIGNMENT_CENTER;
    }
    else 
    {                              /* left justify & clip text */
	alignment = XmALIGNMENT_BEGINNING;
    }
#endif /* WSM */
    
    if (ACTIVE_PSD->cleanText)
    {
	XmStringDrawImage(dpy, w, xmfontlist, xmstring, gc, x, y, width, 
			  alignment, XmSTRING_DIRECTION_L_TO_R, 
			  pbox);
    }
    else
    {
	XmStringDraw (dpy, w, xmfontlist, xmstring, gc, x, y, width, 
		      alignment, XmSTRING_DIRECTION_L_TO_R, pbox);
    }
} /* END OF FUNCTION WmDrawXmString */

#ifdef WSM

/*************************************<->*************************************
 *
 *  WmInstallBitmapIntoXmCache (pchName, bitmap, width, height)
 *
 *
 *  Description:
 *  -----------
 *  Installs all or part of a pixmap into the Xm cache. This pixmap
 *  may be retrieved later by a call to XmGetPixmap.
 *
 *  Inputs:
 *  ------
 *  pchName	= pointer to name of bitmap 
 *  bitmap	= depth-1 pixmap
 *  width 	= width of portion to install
 *  height	= height of portion to install
 * 
 *  Outputs:
 *  -------
 *  none
 *
 *  Comments:
 *  --------
 *  This always installs the Northwest corner of the passed in bitmap.
 *  If the width and height match the size of the bitmap, then the
 *  whole thing is installed in the cache.
 *			
 *************************************<->***********************************/
void WmInstallBitmapIntoXmCache (unsigned char *pchName,
    Pixmap bitmap, unsigned int width, unsigned int height)
{
    XImage *pImage;

    pImage = XGetImage (DISPLAY, bitmap, 0, 0, width, height, 1L, XYBitmap);

    XmInstallImage (pImage, (char *)pchName);
} /* END OF FUNCTION WmInstallBitmapIntoXmCache */



/*************************************<->*************************************
 *
 *  WmInstallBitmapDataIntoXmCache (pSD, pchName, pData)
 *
 *
 *  Description:
 *  -----------
 *  Installs built-in bitmap data into the Xm Pixmap cache. The image
 *  may be retrieved later by a call to XmGetPixmap.
 *
 *  Inputs:
 *  ------
 *  pSD		= pointer to screen data
 *  pchName	= pointer to name of bitmap 
 *  pData	= pointer to the bitmap data
 * 
 *  Outputs:
 *  -------
 *  none
 *
 *  Comments:
 *  --------
 *  This is principally for putting built-in pixmap data into the Xm
 *  cache to allow for uniform access to pixmap creation.
 *
 *  ***WARNING***
 *  Do NOT call XmDestroyPixmap on images cached via this routine unless
 *  pData passed in points to malloc'ed memory. XmDestroyPixmap could 
 *  try to free this data.
 *			
 *************************************<->***********************************/
void WmInstallBitmapDataIntoXmCache (WmScreenData *pSD, 
    unsigned char *pchName, char *pData, unsigned int width,
    unsigned int height)
{
    XImage *pImage;

    if (pImage = (XImage *) XtMalloc (sizeof (XImage)))
    {
	pImage->width = width; 
	pImage->height = height;
	pImage->xoffset = 0;
	pImage->data = pData;
	pImage->format = XYBitmap;
	pImage->byte_order = MSBFirst;
	pImage->bitmap_pad = 8;
	pImage->bitmap_bit_order = LSBFirst;
	pImage->bitmap_unit = 8;
	pImage->depth = 1;
	pImage->bytes_per_line = (width/8) + ((width%8) != 0 ? 1 : 0);
	pImage->obdata = NULL;

	XmInstallImage(pImage, (char *)pchName);
    }

} /* END OF FUNCTION WmInstallBitmapDataIntoXmCache */
#endif /* WSM */
