/* #include <X11/copyright.h> */

/* $XConsortium: Bar.c,v 1.2 88/10/25 17:40:25 swick Exp $ */
/* Copyright	Massachusetts Institute of Technology	1987, 1988 */

#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include "array.h"
#include "BarP.h"

static Dimension def_dimension	= (Dimension)~0;
static XtOrientation def_orient = XtorientHorizontal;
static Boolean def_true = True;

#define DEF_LENGTH  ((Dimension) 200)
#define DEF_THICKNESS	((Dimension) 15)

/*
 * !! should provide converters for Arrays of some supported types, and
 * for fillstyles
 */
static XtResource resources[] = {
#define offset(field) XtOffset(BarWidget, bar.field)
#define Offset(field) XtOffset(BarWidget, field)
    /* {name, class, type, size, offset, default_type, default_addr}, */
    /*
     * We define these core class values to set defaults, so we can
     * initialize correctly
     */
    {XtNwidth, XtCWidth, XtRDimension, sizeof(Dimension),
	Offset(core.width), XtRDimension, (caddr_t)&def_dimension},
    {XtNheight, XtCHeight, XtRDimension, sizeof(Dimension),
	Offset(core.height), XtRDimension, (caddr_t)&def_dimension},
    /* Private bar widget resources */
    {XtNlength, XtCLength, XtRDimension, sizeof(Dimension),
	offset(length), XtRDimension, (caddr_t) &def_dimension},
    {XtNthickness, XtCThickness, XtRDimension, sizeof(Dimension),
	offset(thickness), XtRDimension, (caddr_t) &def_dimension},
    {XtNorientation, XtCOrientation, XtROrientation, sizeof(XtOrientation),
	offset(orientation), XtROrientation, (caddr_t) &def_orient},
    {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
	offset(foreground), XtRString, XtDefaultForeground},
    {XtNfillStyle, XtCFillStyle, XtRInt, sizeof(int),
	offset(fillstyle), XtRInt, (caddr_t) 0},
    {XtNpixmaps, XtCPixmaps, XtRArray, sizeof(Array *),
	offset(pixmaps), XtRArray, (caddr_t) NULL},
#undef offset
#undef Offset
};

static void ClassInitialize();
static void Initialize();
static void Resize();
static void Redisplay();

BarClassRec barClassRec = {
  { /* core fields */
    /* superclass		*/	(WidgetClass) &widgetClassRec,
    /* class_name		*/	"Bar",
    /* widget_size		*/	sizeof(BarRec),
    /* class_initialize		*/	ClassInitialize,
    /* class_part_initialize	*/	NULL,
    /* class_inited		*/	FALSE,
    /* initialize		*/	Initialize,
    /* initialize_hook		*/	NULL,
    /* realize			*/	XtInheritRealize,
    /* actions			*/	NULL,
    /* num_actions		*/	0,
    /* resources		*/	resources,
    /* num_resources		*/	XtNumber(resources),
    /* xrm_class		*/	NULLQUARK,
    /* compress_motion		*/	TRUE,
    /* compress_exposure	*/	TRUE,
    /* compress_enterleave	*/	TRUE,
    /* visible_interest		*/	FALSE,
    /* destroy			*/	NULL,
    /* resize			*/	Resize,
    /* expose			*/	Redisplay,
    /* set_values		*/	NULL,
    /* set_values_hook		*/	NULL,
    /* set_values_almost	*/	XtInheritSetValuesAlmost,
    /* get_values_hook		*/	NULL,
    /* accept_focus		*/	NULL,
    /* version			*/	XtVersion,
    /* callback_private		*/	NULL,
    /* tm_table			*/	NULL,
    /* query_geometry		*/	XtInheritQueryGeometry,
    /* display_accelerator	*/	XtInheritDisplayAccelerator,
    /* extension		*/	NULL
  },
  { /* Bar class fields */
    /* extension		*/	NULL
  }
};

WidgetClass barWidgetClass = (WidgetClass)&barClassRec;

static void
ClassInitialize()
{
    XtAddConverter( XtRString, XtROrientation, XmuCvtStringToOrientation,
		    NULL, (Cardinal)0 );
}

static void
SetDimensions(w)
BarWidget w;
{
    if (w->bar.orientation == XtorientVertical) {
	w->bar.length = w->core.height;
	w->bar.thickness = w->core.width;
    }
    else {
	w->bar.length = w->core.width;
	w->bar.thickness = w->core.height;
    }
}

/* 
 * We can't use XtGetGC, since we're going to write to the GC, so we use
 * a simple caching scheme to share a gc between Bar widgets of the same
 * fillstyle on the same display.  The first Bar widget in gets to be
 * the GC we cache.  For most applications, all Bars will be on one
 * display and use the same fillStyle, we hope.
 */
static GC gc_cache;
static Display *dpy_cache;
static int fstyle_cache;

/* ARGSUSED */
static void
Initialize(request, new)
Widget request;		/* what the client asked for */
Widget new;		/* what we're going to give him */
{
    BarWidget w = (BarWidget) new;
    XGCValues gcv;
    Display *dpy = XtDisplay(new);

    if (dpy_cache == NULL || w->bar.fillstyle != fstyle_cache ||
	dpy_cache != dpy) {
	gcv.fill_style = w->bar.fillstyle;
	gcv.foreground = w->bar.foreground;
	gcv.background = w->core.background_pixel;
	w->bar.gc = XCreateGC(dpy, DefaultRootWindow(dpy),
			      GCFillStyle | GCForeground | GCBackground, &gcv);
	if (dpy_cache == NULL) {
	    dpy_cache = dpy;
	    fstyle_cache = w->bar.fillstyle;
	    gc_cache = w->bar.gc;
	}
    } else {
	w->bar.gc = gc_cache;
    }
    /* Width and height override length and thickness */
    if (w->bar.length == def_dimension)
	w->bar.length = DEF_LENGTH;
	
    if (w->bar.thickness == def_dimension)
	w->bar.thickness = DEF_THICKNESS;
	
    if (w->core.width == def_dimension)
	w->core.width = (w->bar.orientation == XtorientVertical)
	    ? w->bar.thickness : w->bar.length;

    if (w->core.height == def_dimension)
	w->core.height = (w->bar.orientation == XtorientHorizontal)
	    ? w->bar.thickness : w->bar.length;
    w->bar.values = NULL;
    w->bar.nvalues = 0;

    SetDimensions(w);
}

/* ARGSUSED */
static void
Resize(gw)
Widget gw;
{
    SetDimensions((BarWidget)gw);
    Redisplay(gw, (XEvent*)NULL, (Region)NULL);
}

/* ARGSUSED */
static void
Redisplay(gw, event, region)
Widget gw;
XEvent *event;
Region region;
{
    BarWidget w = (BarWidget) gw;
    int x, y;
    unsigned int width, height;
    int maxvalue, i;
    int vertical = (w->bar.orientation == XtorientVertical);;

    if (! XtIsRealized(gw))
	return;
    for(maxvalue = 0, i = 0; i < w->bar.nvalues; i++)
	maxvalue += w->bar.values[i];
    if(maxvalue <= 0)
	return;
    x = y = 0;
    if(vertical)
	width = w->bar.thickness;
    else
	height = w->bar.thickness;
    if (w->bar.nvalues > w->bar.pixmaps->nitems) {
	XtWarning("Too many values to represent in a bar");
	return;
    }
    for(i = 0; i < w->bar.nvalues; i++) {
	unsigned int rectlen = (w->bar.length * w->bar.values[i] +
				maxvalue / 2) / (unsigned int)maxvalue;
	if (rectlen == 0)
	    continue;
	if (i == w->bar.nvalues - 1) {
	    if (vertical)
		rectlen = w->core.height - y;
	    else
		rectlen = w->core.width - x;
	}
	switch (w->bar.fillstyle) {
	case FillSolid:
	    XSetForeground(XtDisplay(gw), w->bar.gc,
			   ((Pixel *) w->bar.pixmaps->items)[i]);
	    break;
	case FillTiled:
	    XSetTile(XtDisplay(gw), w->bar.gc,
			   ((Pixmap *) w->bar.pixmaps->items)[i]);
	    break;
	case FillStippled:
	case FillOpaqueStippled:
	    XSetStipple(XtDisplay(gw), w->bar.gc,
			   ((Pixmap *) w->bar.pixmaps->items)[i]);
	    break;
	}
	if(vertical) {
	    XFillRectangle(XtDisplay(gw), XtWindow(gw), w->bar.gc,
	     x, y, width, rectlen);
	    y += rectlen;
	} else {
	    XFillRectangle(XtDisplay(gw), XtWindow(gw), w->bar.gc,
	     x, y, rectlen, height);
	    x += rectlen;
	}
    }
}

/* !! We really should make the widget support Xt{Get,Set}Values fully */
/* Public routines. */
void
SetBarValues(gw, values, nvalues)
Widget gw;
int *values;
int nvalues;
{
    BarWidget w = (BarWidget) gw;
    int i;

    if (nvalues > w->bar.pixmaps->nitems) {
#define ERRFMT "Tried to set %d values for bar - maximum is %d\n"
	char errbuf[sizeof(ERRFMT) + 32];
	(void) sprintf(errbuf, ERRFMT, nvalues, w->bar.pixmaps->nitems);
#undef ERRFMT
	XtWarning(errbuf);
	nvalues = w->bar.pixmaps->nitems;
    }
    if (nvalues <= 0) {
#define ERRFMT "Tried to set %d values for bar - it must be > 0\n"
	char errbuf[sizeof(ERRFMT) + 32];
	(void) sprintf(errbuf, ERRFMT, nvalues);
	XtWarning(errbuf);
#undef ERRFMT
	return;
    }
    if (w->bar.values == NULL || w->bar.nvalues < nvalues) {
	if (w->bar.values) XtFree((char *) w->bar.values);
	w->bar.values = (int *) XtMalloc(nvalues * sizeof(int));
    }
    w->bar.nvalues = nvalues;
    for(i = 0; i < nvalues; i++)
	w->bar.values[i] = values[i];
    Redisplay( gw, (XEvent*)NULL, (Region)NULL );
}


syntax highlighted by Code2HTML, v. 0.9.1