static	char	rcsid[] = "$Id: Gcs.c,v 1.2 1998/10/12 16:29:32 falk Exp $" ;

/*
 * Gcs.c - Utility functions to allocate GCs.
 *
 * Author: Edward A. Falk
 *	   falk@falconer.vip.best.com
 *
 * Date: Sept 29, 1998
 *
 *
 * $Log: Gcs.c,v $
 * Revision 1.2  1998/10/12 16:29:32  falk
 * Now exports the allocPixel and GetGrey50 functions
 *
 * Revision 1.1  1998/10/12 01:38:14  falk
 * Initial revision
 *
 *
 */

/* Functions:
 *
 * GC AllocFgGC(w, fg, font)
 *	Return a GC with foreground set as specified.
 *	If font is None, then the returned GC is allocated with font specified
 *	as a "don't care" value.
 *
 * GC
 * AllocBackgroundGC(w)
 *	Return a GC with the foreground set to the widget's background color.
 *
 * GC
 * AllocGreyGC(w, fg, font, contrast, gp, be_nice_to_cmap)
 *	Widget	w ;
 *	Pixel	fg ;
 *	Font	font ;
 *	int	contrast ;
 *	Pixmap	*gp ;
 *	int	be_nice_to_cmap ;
 *
 *	Return a GC suitable for rendering a widget in its "inactive" color.
 *	Normally returns a GC with a color somewhere between the widget's
 *	background color and the specified foreground. If font is None, then
 *	the returned GC is allocated with font specified as "don't care".
 *	If be_nice_to_cmap is True, the returned GC is created using a 50%
 *	dither instead of a new color.
 *
 *	The 'gp' argument is a pointer to a Pixmap value, used to cache the
 *	50% dither pattern. If *gp points to a Pixmap value, it will be used
 *	as the source of the 50% dither pattern. If *gp is None, then a
 *	dither pattern will be created and stored in gp. If gp is NULL, then
 *	a dither pattern will be created but not returned.
 *
 * GC
 * AllocTopShadowGC(w, contrast, be_nice_to_cmap)
 *	Return a GC suitable for rendering the "top shadow" decorations of
 *	a widget.  Returns a GC with foreground computed from widget's
 *	background color and contrast.  If be_nice_to_cmap is True, the
 *	returned GC will use a foreground color of white.  If widget depth
 *	is 1, this function will use a foreground color of black.
 *
 * GC
 * AllocBotShadowGC(w, contrast, be_nice_to_cmap)
 *	Return a GC suitable for rendering the "bottom shadow" decorations
 *	of a widget. Returns a GC with foreground computed from widget's
 *	background color and contrast. If be_nice_to_cmap is True, the
 *	returned GC will use a foreground color of black.
 *
 * GC
 * AllocArmGC(w, contrast, gp, be_nice_to_cmap)
 *	Return a GC suitable for rendering the "armed" decorations of a
 *	widget. This GC would typically be used to fill in the widget's
 *	background. Returns a GC with foreground computed from widget's
 *	background color and contrast.  If be_nice_to_cmap is True, the
 *	returned GC will use a foreground color of black and a 50% dither.
 *
 *	The 'gp' argument is as described above.
 *
 * void
 * Draw3dBox(w, x,y,wid,hgt,s, topgc, botgc)
 *	Utility function.  Draws a raised shadow box with outside dimensions
 *	as specified by x,y,wid,hgt and shadow width specified by s.
 *	A lowered shadow box may be generated by swapping topgc and botgc.
 *
 */

#include	<stdio.h>

#include	<X11/Xlib.h>
#include	<X11/IntrinsicP.h>
#include	<X11/StringDefs.h>
#include	<X11/Xmu/Misc.h>

#include	"Gcs.h"

	/* Color & GC allocation.
	 *
	 * Frame widgets use the following graphics contexts:
	 *
	 *  Foreground		tab label text drawn this way
	 *  Insensitive Fg	foreground color greyed out.
	 *  Background		frame background color
	 *  Top shadow		upper-left highlight around widget
	 *  Bottom shadow	lower-right highlight around widget
	 *  Arm shadow		button pressed and ready to be released
	 *
	 *
	 * GC's are defined as follows, depending on attributes and
	 * window depth:
	 *
	 * Monochrome:
	 *	Foreground = foreground color attribute or BlackPixel()
	 *	Grey = Foreground color + 50% dither
	 *	Background = background color attribute or WhitePixel()
	 *	top shadow = foreground
	 *	bottom shadow = foreground
	 *	arm shadow = (what?)
	 *
	 * Color, beNiceToColormap=true:
	 *	Foreground = foreground color attribute or BlackPixel()
	 *	Grey = Foreground color + 50% dither
	 *	Background = background color attribute or WhitePixel()
	 *	top shadow = white
	 *	bottom shadow = black
	 *	arm shadow = (what?)
	 *
	 * Color, beNiceToColormap=false:
	 *	Foreground = foreground color attribute or BlackPixel()
	 *	Grey = (foreground color + background color)/2
	 *	Background = background color attribute or WhitePixel()
	 *	top shadow = background * 1.2
	 *	bottom shadow = background * .6
	 *	arm shadow = background * .8
	 *
	 * Special cases:
	 *	If background is white,   ??
	 *	if background is black,   ??
	 *
	 *
	 * If the widget's background is solid white or solid black,
	 * this code just picks some numbers.  (The choice is designed
	 * to be compatibile with ThreeD interface.)
	 */



#if	XtSpecificationRelease	< 5

static	GC	XtAllocateGC(Widget, int, u_long, XGCValues *, u_long, u_long) ;

#endif

static	char	shadowpm_bits[] = {0x02, 0x01};



	/* return a GC with the specified foreground and optional font */

GC
AllocFgGC(w, fg, font)
	Widget	w;
	Pixel	fg ;
	Font	font ;
{
	XGCValues	values ;
	u_long		vmask, dcmask ;

	values.foreground = fg ;
	values.font = font ;

	if( font != None ) {
	  vmask = GCForeground|GCFont ;
	  dcmask = GCSubwindowMode|GCGraphicsExposures|GCDashOffset|
		GCDashList|GCArcMode|GCBackground ;
	} else {
	  vmask = GCForeground ;
	  dcmask = GCFont|GCSubwindowMode|GCGraphicsExposures|GCDashOffset|
		GCDashList|GCArcMode|GCBackground ;
	}

	return XtAllocateGC(w, w->core.depth, vmask, &values, 0L, dcmask) ;
}


	/* return gc with widget background color as the foreground */

GC
AllocBackgroundGC(w)
	Widget	w;
{
	XGCValues	values ;

	values.foreground = w->core.background_pixel ;

	return XtAllocateGC(w, w->core.depth,
	    GCForeground, &values,
	    0L,
	    GCBackground|GCFont|GCSubwindowMode|GCGraphicsExposures|
	    	GCDashOffset|GCDashList|GCArcMode) ;
}


	/* Allocate an "inactive" GC.  Color is grey (possibly via
	 * dither pattern).  This function optionally returns the
	 * grey pixmap so the caller may cache it.
	 */

GC
AllocGreyGC(w, fg, font, contrast, gp, be_nice_to_cmap)
	Widget	w ;
	Pixel	fg ;
	Font	font ;
	int	contrast ;
	Pixmap	*gp ;			/* optional grey50 pixmap cache */
	int	be_nice_to_cmap ;
{
	XGCValues	values ;
	u_long		vmask, dcmask ;

	values.foreground = fg ;
	values.background = w->core.background_pixel ;
	values.font = font ;

	if( font != None ) {
	  vmask = GCForeground|GCFont ;
	  dcmask = GCSubwindowMode|GCGraphicsExposures|GCDashOffset|
		GCDashList|GCArcMode ;
	} else {
	  vmask = GCForeground ;
	  dcmask = GCFont|GCSubwindowMode|GCGraphicsExposures|GCDashOffset|
		GCDashList|GCArcMode ;
	}

	if( be_nice_to_cmap || w->core.depth == 1)
	{
	  values.fill_style = FillStippled ;
	  values.stipple = GetGrey50(w,gp) ;

	  vmask |= GCBackground|GCStipple|GCFillStyle ;
	  return XtAllocateGC(w, w->core.depth, vmask, &values, 0L, dcmask) ;
	}
	else
	{
	  values.foreground =
	  	AllocGreyPixel(w, fg, values.background, contrast) ;
	  dcmask |= GCBackground ;
	  return XtAllocateGC(w, w->core.depth, vmask, &values, 0L, dcmask) ;
	}
}

	/* return top-shadow gc. */

GC
AllocTopShadowGC(w, contrast, be_nice_to_cmap)
	Widget	w;
	int	contrast ;
	int	be_nice_to_cmap ;
{
	Screen		*scr = XtScreen (w);
	XGCValues	values ;

	if( w->core.depth == 1 )
	  values.foreground = BlackPixelOfScreen(scr) ;
	else if( be_nice_to_cmap )
	  values.foreground = WhitePixelOfScreen(scr) ;
	else
	  values.foreground = AllocShadowPixel(w, 100+contrast) ;

	return XtAllocateGC(w, w->core.depth,
	    GCForeground, &values,
	    0L,
	    GCBackground|GCFont|GCSubwindowMode|GCGraphicsExposures|
		GCDashOffset|GCDashList|GCArcMode) ;
}

	/* return bottom-shadow gc. */

GC
AllocBotShadowGC(w, contrast, be_nice_to_cmap)
	Widget	w ;
	int	contrast ;
	int	be_nice_to_cmap ;
{
	Screen		*scr = XtScreen (w);
	XGCValues	values ;

	if( w->core.depth == 1 || be_nice_to_cmap )
	  values.foreground = BlackPixelOfScreen(scr) ;
	else
	  values.foreground = AllocShadowPixel(w, 100-contrast) ;

	return XtAllocateGC(w, w->core.depth,
	    GCForeground, &values,
	    0L,
	    GCBackground|GCFont|GCSubwindowMode|GCGraphicsExposures|
		GCDashOffset|GCDashList|GCArcMode) ;
}

	/* return arm-shadow gc. */

GC
AllocArmGC(w, contrast, gp, be_nice_to_cmap)
	Widget	w;
	int	contrast ;
	Pixmap	*gp ;			/* optional grey50 pixmap cache */
	int	be_nice_to_cmap ;
{
	Screen		*scr = XtScreen (w);
	XGCValues	values ;

	/* Not clear exactly what we should do here.  Take a look at
	 * Xaw3d to see what they do.
	 */

	if( w->core.depth == 1 || be_nice_to_cmap )
	{
	  values.background = w->core.background_pixel ;
	  if( values.background == BlackPixelOfScreen(scr) )
	    values.foreground = WhitePixelOfScreen(scr) ;
	  else
	    values.foreground = BlackPixelOfScreen(scr) ;
	  values.fill_style = FillStippled ;
	  values.stipple = GetGrey50(w,gp) ;

	  return XtAllocateGC(w, w->core.depth,
	      GCForeground|GCBackground|GCStipple|GCFillStyle,
	      &values, 0L,
	      GCFont|GCSubwindowMode|GCGraphicsExposures|
		  GCDashOffset|GCDashList|GCArcMode) ;
	}
	else {
	  values.foreground = AllocShadowPixel(w, 100-contrast) ;
	  return XtAllocateGC(w, w->core.depth,
	      GCForeground, &values,
	      0L,
	      GCBackground|GCFont|GCSubwindowMode|GCGraphicsExposures|
		  GCDashOffset|GCDashList|GCArcMode) ;
	}
}


Pixel
AllocShadowPixel(w, scale)
	Widget	w;
	int	scale ;
{
	XColor	get_c, set_c ;
	Display	*dpy = XtDisplay(w) ;
	Screen	*scr = XtScreen(w) ;
	Colormap cmap ;
	Pixel	maxColor ;

	cmap = w->core.colormap ;

	get_c.pixel = w->core.background_pixel ;
	if( get_c.pixel == WhitePixelOfScreen(scr)  ||
	    get_c.pixel == BlackPixelOfScreen(scr) )
	{
	  /* what we *ought* to do is choose gray75 as the base color,
	   * or perhaps gray83.  Instead, we choose colors that are
	   * the same as ThreeD would choose.
	   */
	  if( scale > 100 )	scale = 200 - scale ;
	  set_c.red = set_c.green = set_c.blue = 65535*scale/100 ;
	}
	else
	{
	  XQueryColor(dpy, cmap, &get_c) ;
	  /* adjust scale so that brightest component does not
	   * exceed 65535; otherwise hue would change.
	   */
	  if( scale > 100 ) {
	    maxColor = Max(get_c.red, Max(get_c.green, get_c.blue)) ;
	    if( scale*maxColor > 65535*100 )
	      scale = 65535*100/maxColor ;
	  }
	  set_c.red = scale * get_c.red / 100 ;
	  set_c.green = scale * get_c.green / 100 ;
	  set_c.blue = scale * get_c.blue / 100 ;
	}
	if( XAllocColor(dpy, cmap, &set_c) )
	  return set_c.pixel ;
	else if( scale > 100 )
	  return WhitePixelOfScreen(scr) ;
	else
	  return BlackPixelOfScreen(scr) ;
}


	/* Allocate a pixel halfway between foreground and background */

Pixel
AllocGreyPixel(w, fg, bg, cnt)
	Widget	w ;
	Pixel	fg, bg ;
	int	cnt ;
{
	XColor	get_cf, get_cb, set_c ;
	Display	*dpy = XtDisplay(w) ;
	Colormap cmap ;

	cmap = w->core.colormap ;

	get_cf.pixel = fg ;
	get_cb.pixel = bg ;

	XQueryColor(dpy, cmap, &get_cf) ;
	XQueryColor(dpy, cmap, &get_cb) ;

	set_c.red =   (get_cf.red * cnt +   get_cb.red * (100-cnt)) / 100 ;
	set_c.green = (get_cf.green * cnt + get_cb.green * (100-cnt)) / 100 ;
	set_c.blue =  (get_cf.blue * cnt +  get_cb.blue * (100-cnt)) / 100 ;

	(void)XAllocColor(dpy, cmap, &set_c) ;
	return set_c.pixel ;
}




Pixmap
GetGrey50(w, gp)
	Widget	w;
	Pixmap	*gp ;
{
	Pixmap	pm = gp != NULL ? *gp : None ;
	if( pm == None ) {
	  pm = XCreateBitmapFromData( XtDisplay(w),
			    XtWindow(w),
			    shadowpm_bits, 2, 2) ;
	  if( gp != NULL ) *gp = pm ;
	}

	return pm ;
}





	/* draw a 3-d box */

void
Draw3dBox(w, x,y,wid,hgt,s, topgc, botgc)
	Widget		w ;
	int		x,y ;		/* position */
	int		wid,hgt,s ;	/* outside dimensions, shadow wid */
	GC		topgc ;
	GC		botgc ;
{
	Display		*dpy = XtDisplay(w) ;
	Window		win = XtWindow(w) ;

	if( s == 0 ) return ;

	if( s == 1 ) {
	  XDrawLine(dpy,win,botgc, x,y+hgt-1, x+wid-1,y+hgt-1) ;
	  XDrawLine(dpy,win,botgc, x+wid-1,y, x+wid-1,y+hgt-1) ;
	  XDrawLine(dpy,win,topgc, x,y, x,y+hgt-1) ;
	  XDrawLine(dpy,win,topgc, x,y, x+wid-1,y) ;
	}
	else
	{
	  XPoint pts[6] ;

	  /* bottom-right shadow */
	  pts[0].x = x ;	pts[0].y = y + hgt ;
	  pts[1].x = s ;	pts[1].y = -s ;
	  pts[2].x = wid-2*s ;	pts[2].y = 0 ;
	  pts[3].x = 0 ;	pts[3].y = -(hgt-2*s) ;
	  pts[4].x = s ;	pts[4].y = -s ;
	  pts[5].x = 0 ;	pts[5].y = hgt ;
	  XFillPolygon(dpy,win,botgc, pts,6, Nonconvex,CoordModePrevious) ;

	  /* top-left shadow */
	  pts[0].x = x ;	pts[0].y = y ;
	  pts[1].x = wid ;	pts[1].y = 0 ;
	  pts[2].x = -s ;	pts[2].y = s ;
	  pts[3].x = -wid+2*s ;	pts[3].y = 0 ;
	  pts[4].x = 0 ;	pts[4].y = hgt-2*s ;
	  pts[5].x = -s ;	pts[5].y = s ;
	  XFillPolygon(dpy,win,topgc, pts,6, Nonconvex,CoordModePrevious) ;
	}
}

#if XtSpecificationRelease < 5

static	GC
XtAllocateGC(w, depth, mask, values, dynamic, dontcare)
	Widget		w ;
	int		depth ;
	unsigned long	mask, dynamic, dontcare ;
	XGCValues	*values ;
{
	return XtGetGC(w, mask, values) ;
}
#endif
