/*
 *	$Source: /u1/X/DECToolkit/src/RCS/Valuator.c,v $
 *	$Header: Valuator.c,v 1.1 86/12/17 09:04:00 swick Exp $
 */

#ifndef lint
static char *rcsid_Valuator_c = "$Header: Valuator.c,v 1.1 86/12/17 09:04:00 swick Exp $";
#endif	lint

#ifndef lint
static char *sccsid = "@(#)Valuator.c	1.6          12/11/86";
#endif lint
/*
 *			  COPYRIGHT 1986
 *		   DIGITAL EQUIPMENT CORPORATION
 *		       MAYNARD, MASSACHUSETTS
 *			ALL RIGHTS RESERVED.
 *
 * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
 * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
 * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
 * ANY PURPOSE.  IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
 *
 * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT RIGHTS,
 * APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN ADDITION TO THAT
 * SET FORTH ABOVE.
 *
 *
 * 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 and that both that
 * copyright notice and this permission notice appear in supporting documentation,
 * and that the name of Digital Equipment Corporation not be used in advertising
 * or publicity pertaining to distribution of the software without specific, 
 * written prior permission.
 */


/*
 * 
 * Author: K. Langone
 *
 */

#include <stdio.h>
#include <X/Xlib.h>
#include "Toolkit.h"

/* Private Definitions */

#define valuatorEvents (LeaveWindow | EnterWindow | ButtonPressed | \
	ButtonReleased | LeftDownMotion | ExposeWindow) 
#define valbuttonEvents (ExposeRegion | ExposeWindow | \
 	ButtonPressed | ButtonReleased) 

#define IgnoreEvent - 1
#define sizedif 2

typedef struct _ValuatorData {
    Window tw;			/* valuator tool window */
    Window pw;			/* parent window, needed for resizing */
    int     wheight;		/* original height of parent window */
    int     wwidth;		/* original width of parent window */
    int     type;		/* type - visible or transparent */
    enum Direction orientation; /* vertical - horizontal */
    int     maxdim;		/* max width of valuator */
    int     mindim;		/* minimum dimension */
    int     x, y;		/* position relative to parent window */
    float   indpos;		/* indicator pos. on valuator(0.0-1.0) */
    int     indx, indy;		/* integer pixel position */
    int     indsize;		/* height of indicator */
    int     brwidth;		/* border width */
    Pixmap brcolor;	   	/* border color index */
    Pixmap background;		/* pixmap of background */
    Pixmap indcolor;		/* indicator color index */
    int     (*Proc) ();		/* procedure called when ind. is moved */
    caddr_t tag;		/* additional tool data */
} ValuatorData;

static int  valuatorEntry;
static int  initDone = 0;
extern char *malloc ();
float  *ptr;

/* =================================================================== */
/* Private Procedures */


static  Initialize () {
            valuatorEntry = UniqueEntryType ();
    initDone = 1;
}

static Dummy()
{
	printf("dummy call back for Valuator\n");
}

/* -----> Utilities */


/* =================================================================== */

/* -----> Utilities */

/*  This procedure will move the indicator using
 *  two XTileSet calls
 *  If the indx or indy = IgnoreEvent the
 *  current indicator position is not updated ,
 *  this condition will occur on expose window events or
 *  changing the indicator size
 */

static  void MoveTheIndicator (data, event)
        ValuatorData * data;
XButtonPressedEvent * event;

{


    int     newpos, update, delta, deltax, deltay;

 /* -----> check to see if indicator should be moved */

    update = 1;


    if (data -> orientation == Horizontal) {
    /* -----> move indicator pixmap horizontally */

	if (data -> indx == IgnoreEvent) {
	/* ignore input event */
	    update = 0;
	    data -> indx =
		(int) (data -> indpos * (data -> maxdim - data -> indsize));

	    if (data -> type == Visible) {
	    /* don't need to clear */
		XTileSet (data -> tw, data -> indx, data -> indy,
			data -> indsize, data -> mindim, data -> indcolor);
	    }

	}

	else
	    if (event -> x >= data -> maxdim - data -> indsize)
				/* got input at end of scroll bar */
		newpos = data -> maxdim - data -> indsize;

	    else
		if (event -> x < 0)/* possible error */
		    newpos = 0;
		else		/* valid input for scroll bar */
		    newpos = event -> x;


    /* ----->Calculate the clear and draw areas */


	if (update & (data -> type == Visible)) {
	    delta = abs (newpos - data -> indx);
	    deltax = data -> indsize - delta;
	    if (abs (newpos - data -> indx) >= data -> indsize) {
	    /* clear all of the indicator */
		XTileSet (data -> tw, newpos, data -> indy,
			data -> indsize, data -> mindim,
			data -> indcolor);
		XTileSet (data -> tw, data -> indx, data -> indy,
			data -> indsize, data -> mindim,
			data -> background);
	    }
	    else
		if (newpos < data -> indx) {
		    XTileSet (data -> tw, newpos, data -> indy,
			    delta, data -> mindim,
			    data -> indcolor);
		    XTileSet (data -> tw, data -> indx + deltax,
			    data -> indy, delta, data -> mindim, 
			    data -> background);
		}
		else {		/* newpos > data->indx */
		    XTileSet (data -> tw, newpos + deltax, data -> indy,
			    delta, data -> mindim, data -> indcolor);
		    XTileSet (data -> tw, data -> indx, data -> indy,
			    delta, data -> mindim, data -> background);
		}
	/* update indicator position */
	    data -> indx = newpos;
	}

    }				/* end up/down scroll bar */

    else {
    /* -----> move indicator pixmap vertically */

	if (data -> indy == IgnoreEvent) {
	/* ignore input event */
	    update = 0;
	    data -> indy =
		(int) (data -> indpos * (data -> maxdim - data -> indsize));

	/* don't need to clear */
	    if (data -> type == Visible) {
		XTileSet (data -> tw, data -> indx, data -> indy,
			data -> mindim, data -> indsize, data -> indcolor);
	    }
	}

	else
	    if (event -> y >= data -> maxdim - data -> indsize)
				/* got input at end of scroll bar */
		newpos = data -> maxdim - data -> indsize;

	    else
		if (event -> y < 0)/* possible error */
		    newpos = 0;
		else		/* valid input for scroll bar */
		    newpos = event -> y;


    /* ----->Calculate the clear and draw areas */


	if (update & (data -> type == Visible)) {
	    delta = abs (newpos - data -> indy);
	    deltay = data -> indsize - delta;
	    if (abs (newpos - data -> indy) >= data -> indsize) {
	    /* clear all of the indicator */
		XTileSet (data -> tw, data -> indx, newpos,
			data -> mindim, data -> indsize,
			data -> indcolor);
		XTileSet (data -> tw, data -> indx, data -> indy,
			data -> mindim, data -> indsize,
			data -> background);
	    }
	    else
		if (newpos < data -> indy) {
		    XTileSet (data -> tw, data -> indx, newpos,
			    data -> mindim, delta,
			    data -> indcolor);
		    XTileSet (data -> tw, data -> indx,
			    data -> indy + deltay,
			    data -> mindim, delta, data -> background);
		}
		else {		/* newpos > data->indy */
		    XTileSet (data -> tw, data -> indx, newpos + deltay,
			    data -> mindim, delta, data -> indcolor);
		    XTileSet (data -> tw, data -> indx, data -> indy,
			    data -> mindim, delta, data -> background);
		}
	/* update indicator position */
	    data -> indy = newpos;
	}

    }
 /* -----> Change the floating point relative indicator position */
    if (update) {
	data -> indpos = 
	(float) newpos / (float) (data -> maxdim - 2 * data -> brwidth);
    }

}

/* =================================================================== */

/* This procedure reports the indicator position
 * in floating point to the Proc
 */

static  void ReportThumbPosition (data, event)
        ValuatorData * data;
XButtonPressedEvent * event;


{
    float   finalvalue;
    caddr_t tag;

    if (data -> orientation == Horizontal) {
	if (event -> x >= data -> maxdim - (int) data -> indsize)
	    finalvalue = 1.0;
	else
	    if (event -> x < 0)
		finalvalue = 0.0;
	    else
		finalvalue = (float) event -> x /
		    ((float) (data -> maxdim - data -> indsize));
    }
    else {
	if (event -> y >= data -> maxdim - (int) data -> indsize)
	    finalvalue = 1.0;
	else
	    if (event -> y < 0)
		finalvalue = 0.0;
	    else
		finalvalue = (float) event -> y /
		    ((float) (data -> maxdim - data -> indsize));
    }

 /* -----> call the client procedure */
    data -> Proc (finalvalue, data -> tag);

}
/* =================================================================== */
/*
 *
 * This procedure will resize the valuator scroll bar
 */
Status SetIndSize (data, indsize)
ValuatorData * data;
float   indsize;		/* indicator size as portion of valuator*/ 

{

    Status status;

 /* -----> Calulate the indicator size in pixels */
    data -> indsize = (int) (indsize * (float) data -> maxdim);

}

/* =================================================================== */

/* This procedure will configure the valuator with
 * the new dimensions and update the parent window
 * dimensions in the valuator data structure
 */

static  void ConfigureValuator (data, x, y, width, height)
        ValuatorData * data;
int     x, y;
int     width;
int     height;

{

    float   delta;

 /* -----> Proportion the Valuator data to the new values */

    if (data -> orientation == Horizontal) {
	delta = (float) width / (float) data -> wwidth;
	data -> maxdim = delta * (float) data -> maxdim;
    /* SetIndSize(data, delta); */
	XConfigureWindow (data -> tw, data -> x, data -> y,
		data -> maxdim, data -> mindim);
    }
    else {
	delta = (float) height / (float) data -> wheight;
	data -> maxdim = delta * (float) data -> maxdim;
    /* SetIndSize(data, delta); */
	XConfigureWindow (data -> tw, data -> x, data -> y,
		data -> mindim, data -> maxdim);
    }


    XFlush ();



}

/* =================================================================== */

/* The procedure checks to see if the window size was 
 * and will invoke ConfigureValuator if necessary
 */

static  void CheckForResize (data, event)
        ValuatorData * data;
XEvent * event;

{
    WindowInfo info;

 /* -----> get parent window information */
    XQueryWindow (data -> pw, &info);

    XFlush ();


    if (info.width != data -> wwidth || info.height != data -> wheight) {
    /* resize the window */
	ConfigureValuator (data, info.x, info.y,
		info.width, info.height);
	if (data -> orientation == Horizontal) {
	/* update the current window dimensions */
	    data -> wwidth = info.width;
	    data -> wheight = info.height;
	}
	else {
	/* update the current window dimensions */
	    data -> wwidth = info.width;
	    data -> wheight = info.height;
	}
    }

}
/* =================================================================== */

/* This procedure will process the Scroll Bar events
 * and given the event will invoke routines to
 * move the indicator and check for resize 
 */

static int  TProcessValuatorEvent (event)
            XEvent * event;
{
    int     x, y, subW, error;
    int     returnCode;
    short   state;
    ValuatorData * data;
    Status status;


    error = FindEntry (event -> window, valuatorEntry, &data);


    if (error != ERRNONE)
	return (NOTHANDLED);
    returnCode = PROCESSED;
    switch (event -> type) {
	case MouseMoved: 
	    MoveTheIndicator (data, event);
	case ButtonPressed: 
	    MoveTheIndicator (data, event);
	    ReportThumbPosition (data, event);
	    break;
	case ButtonReleased: 
	    MoveTheIndicator (data, event);
	    ReportThumbPosition (data, event);
	    break;
	case ExposeWindow: 
	    if (data -> orientation == Horizontal)
		data -> indx = IgnoreEvent;
	    else
		data -> indy = IgnoreEvent;

	    MoveTheIndicator (data, event);
	case EnterWindow: 
	/* status = XQueryMouseButtons(event->window, &x, &y, &subW,
	   &state); if (state & LeftMask) DisplayCommand(event->window,
	   data, TRUE); break; */
	case LeaveWindow: 
	/* MoveTheIndicator(data, event); */
	    break;
	default: 
	    returnCode = NOTHANDLED;
    }
    return (returnCode);
}


/* Public Procedures */
/* =================================================================== */
/*
 * This procedure will reposition the indicator
 * and can be called from the application
 */


Status TSetIndPosition (tw, indpos)
Window tw;			/* valuator tool window */
float   indpos;			/* indicator position on valuator(0.0-1.0) 
				*/
{

    ValuatorData * data;
    int     error;
    Status status;
    float   movelength;
    WindowInfo info;

    error = FindEntry (tw, valuatorEntry, &data);
 /* put in error check on indpos later (0.0-1.0) */

    movelength = (float) (data -> maxdim) - data -> indsize;

 /* if (data->orientation == Horizontal) else */

 /* -----> change the value in the data structure */
    data -> indpos = indpos;
    status = 1;

}

/* =================================================================== */
/*
 *
 * This procedure will resize the valuator indicator
 */
Status TSetIndSize (tw, indsize)
Window tw;			/* valuator tool window */
float   indsize;		/* indicoatr size */
{

    ValuatorData * data;
    XEvent * event;		/* this is only used as a pointer */
    int     error;
    Status status;


 /* -----> Error check for appropriate indsize */
 /* if (indsize > 1.0) or (indsize < 0.0) then report error */

 /* -----> Get valuator data */
    error = FindEntry (tw, valuatorEntry, &data);

    status = SetIndSize (data, indsize);

}

/* =================================================================== */
/*
 *
 * This procedure will refresh the Valuator
 */
Status TRefreshValuator (tw)
Window tw;			/* valuator tool window */
{

    ValuatorData * data;
    XEvent * event;		/* this is only used as a pointer */
    int     error;
    Status status;

 /* -----> Get the data */
    error = FindEntry (tw, valuatorEntry, &data);
 /* -----> Set Ignore Event flag */
    if (data -> orientation == Horizontal)
	data -> indx = IgnoreEvent;
    else
	data -> indy = IgnoreEvent;

 /* -----> Invoke procedure to display new indicator size */
    MoveTheIndicator (data, event);
}

/* =================================================================== */
/*
 * This procedure will reconfigure the valuator
 * and obtain the tool's data
 * Note: if Horizontal Scroll bar - width = long dimension
 *       else for Left/Right Scroll bar - height = long dimension
 */


Status TConfigureValuator (tw, x, y, width, height)
Window tw;			/* valuator tool window */
int     x, y;			/* new window position */
int     width;			/* new valuator width */
int     height;			/* new valuator height */
{

    ValuatorData * data;
    int     error;
    float   indsize;
    Status status;

 /* -----> Get the valuator data */
    error = FindEntry (tw, valuatorEntry, data);

 /* -----> Proportionally Change the Thumbsize */
    if (data -> orientation == Horizontal)
	indsize = (float) width / (float) data -> maxdim;
    else
	indsize = (float) height / (float) data -> maxdim;

    SetIndSize (data, indsize);

    ConfigureValuator (data, x, y, width, height);
}

/* =================================================================== */
/*
 * This procedure will destroy a valuator 
 *
 */

Status TDestroyValuator (tw)
Window tw;
{

 /* -----> Delete the data in the table */
    DeleteEntry (tw, valuatorEntry);

 /* Other code, tbd... */
}


/* =================================================================== */

/*
 * This procedure will create a command button and will set predefined
 * values colors,font,and state selection. If any errors are detected
 * during the parsing of the input arguments, an exit will occur.
 *
 */

Status TGetValuatorAttr (tw, arglist)
Window tw;			/* tool window */
Targ * arglist;
{
    extern  FontInfo * gfontinfo;
    ValuatorData * data;
    int     return_code;	/* return code from window creation */
    int     i, error;

    error = FindEntry (tw, valuatorEntry, &data);

 /* -----> Parsing input parameters */

    while (arglist -> name) {
	switch (arglist -> name) {
	    case T_VALUATOR_TYPE: 
		arglist -> data = (caddr_t) data -> type;
		break;
	    case T_VALUATOR_ORIENT: 
		arglist -> data = (caddr_t) data -> orientation;
		break;
	    case T_VALUATOR_MAXDIM: 
		arglist -> data = (caddr_t) data -> maxdim;
		break;
	    case T_VALUATOR_MINDIM: 
		arglist -> data = (caddr_t) data -> mindim;
		break;
	    case T_VALUATOR_INDPOS: 
		arglist -> data = (caddr_t) & data -> indpos;
		break;
	    case T_VALUATOR_INDSIZE: 
		arglist -> data = (caddr_t) data -> indsize;
		break;
	    case T_VALUATOR_BRCOLOR: 
		arglist -> data = (caddr_t) data -> brcolor;
		break;
	    case T_VALUATOR_BACKGROUND: 
		arglist -> data = (caddr_t) data -> background;
		break;
	    case T_VALUATOR_BRWIDTH: 
		arglist -> data = (caddr_t) data -> brwidth;
		break;
	    case T_VALUATOR_INDCOLOR: 
		arglist -> data = (caddr_t) data -> indcolor;
		break;
	    case T_VALUATOR_PROC: 
		arglist -> data = (caddr_t) data -> Proc;
		break;
	    case T_VALUATOR_TAG: 
		arglist -> data = (caddr_t) data -> tag;
		break;
	    default: 
	    /* eventually put in error message */
		break;
	}
	arglist++;
    }


}
/* =================================================================== */

/*
 * This procedure will create a command button and will set predefined
 * values colors,font,and state selection. If any errors are detected
 * during the parsing of the input arguments, an exit will occur.
 *
 */

Status TSetValuatorAttr (tw, arglist)
Window tw;			/* tool window */
Targ * arglist;
{
    extern  FontInfo * gfontinfo;
    ValuatorData * data;
    int     return_code;	/* return code from window creation */
    int     twidth, theight;
    int     error;

    error = FindEntry (tw, valuatorEntry, &data);

 /* -----> Parsing input parameters */

    while (arglist -> name) {
	switch (arglist -> name) {
	    case T_VALUATOR_TYPE: 
		data -> type = (int) arglist -> data;
		break;
	    case T_VALUATOR_ORIENT: 
		data -> orientation = (enum Direction) arglist -> data;
		break;
	    case T_VALUATOR_MAXDIM: 
		data -> maxdim = (int) arglist -> data;
		break;
	    case T_VALUATOR_MINDIM: 
		data -> mindim = (int) arglist -> data;
		break;
	    case T_VALUATOR_INDPOS: 
		ptr = (float *) arglist -> data;
		data -> indpos = *ptr;
		break;
	    case T_VALUATOR_INDSIZE: 
		data -> indsize = (int) arglist -> data;
		break;
	    case T_VALUATOR_BRCOLOR: 
		data -> brcolor = (Pixmap) arglist -> data;
		break;
	    case T_VALUATOR_BACKGROUND: 
		data -> background = (Pixmap) arglist -> data;
		break;
	    case T_VALUATOR_BRWIDTH: 
		data -> brwidth = (int) arglist -> data;
		break;
	    case T_VALUATOR_INDCOLOR: 
		data -> indcolor = (int) arglist -> data;
		break;
	    case T_VALUATOR_PROC: 
		data -> Proc = (int (*) ()) arglist -> data;
		break;
	    case T_VALUATOR_TAG: 
		data -> tag = arglist -> data;
		break;
	    default: 
		break;
	}
	arglist++;
    }


 /* if (data->type == 0) DisplayValuatorTx(tw, data, 0); else
    DisplayValuatorPx(tw, data, 0); what to put in here? */

}

/* =================================================================== */

/*
 *
 * This procedure will create a valuator
 *
 */

Window TCreateValuator (pw, arglist)
Window pw;			/* parent window */
Targ * arglist;			/* argument list */

{
    ValuatorData * data;
    WindowInfo info;
    Status status;


 /* -----> Perform initialization */
    if (!initDone)
	Initialize ();

 /* -----> Allocate space for data structure */
    data = (ValuatorData *) Tmalloc (sizeof (ValuatorData));

 /* -----> Set default values */
    data -> type = Visible;
    data -> orientation = Vertical;
    data -> x = 0;
    data -> y = 0;
    data -> indpos = 0.0;
    data -> indsize = 10;
    data -> brwidth = 2;
    data -> brcolor = BlackPixmap;
    data -> background = WhitePixmap;
    data -> indcolor = BlackPixmap;
    data -> maxdim = 70;
    data -> mindim = 10;
    data -> Proc = Dummy;
    data -> tag = NULL;


 /* -----> Get parent window dimensions */
    status = XQueryWindow (pw, &info);

    data -> wheight = info.height;
    data -> wwidth = info.width;

 /* -----> Parsing input parameters */

    while (arglist -> name) {
	switch (arglist -> name) {
	    case T_VALUATOR_TYPE: 
		data -> type = (int) arglist -> data;
		break;
	    case T_VALUATOR_ORIENT: 
		data -> orientation = (enum Direction) arglist -> data;
		break;
	    case T_VALUATOR_MAXDIM: 
		data -> maxdim = (int) arglist -> data;
		break;
	    case T_VALUATOR_MINDIM: 
		data -> mindim = (int) arglist -> data;
		break;
	    case T_VALUATOR_INDPOS: 
		ptr = (float *) arglist -> data;
		data -> indpos = *ptr;
		break;
	    case T_VALUATOR_INDSIZE: 
		data -> indsize = (int) arglist -> data;
		break;
	    case T_VALUATOR_BRCOLOR: 
		data -> brcolor = (Pixmap) arglist -> data;
		break;
	    case T_VALUATOR_BACKGROUND: 
		data -> background = (Pixmap) arglist -> data;
		break;
	    case T_VALUATOR_BRWIDTH: 
		data -> brwidth = (int) arglist -> data;
		break;
	    case T_VALUATOR_INDCOLOR: 
		data -> indcolor = (int) arglist -> data;
		break;
	    case T_VALUATOR_PROC: 
		data -> Proc = (int (*) ()) arglist -> data;
		break;
	    case T_VALUATOR_TAG: 
		data -> tag = arglist -> data;
		break;
	    default: 
		break;
	}
	arglist++;
    }

 /* -----> Initialzie indicator position to "no update" */
    if (data -> orientation == Horizontal) {
	data -> indx = -1;
	data -> indy = 0;
    }
    else {
	data -> indy = -1;
	data -> indx = 0;
    }

 /* -----> Given the dimensions, create the valuator window */
    if (data -> type == Invisible) {
	if (data -> orientation == Horizontal)
	    data -> tw = XCreateTransparency (pw, data -> x, data -> y,
		    data -> maxdim, data -> mindim);
	else
	    data -> tw = XCreateTransparency (pw, data -> x, data -> y,
		    data -> mindim, data -> maxdim);
    }

    else {
	if (data -> orientation == Horizontal) {
	    data -> tw = XCreateWindow (pw, data -> x, data -> y,
		    data -> maxdim, data -> mindim, data -> brwidth,
		    data -> brcolor, data -> background);
	}
	else {
	    data -> tw = XCreateWindow (pw, data -> x, data -> y,
		    data -> mindim, data -> maxdim, data -> brwidth,
		    data -> brcolor, data -> background);
	}
    }
 /* -----> Save the valuator entry, select input, and save the event
    handler */

    SaveEntry (data -> tw, valuatorEntry, data);
    TSetXEventDispatch (data -> tw, TProcessValuatorEvent, valuatorEvents, 0);

    return (data -> tw);

}






