/**************************************************************************
 *
 *************************************************************************/

#include "gemfintl.h"

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

static OBJECT nslider_objects[]  = {
 { -1,	1,	4, G_IBOX,	   NONE,	   NORMAL,	 (_Ob_spec_t)0x00001181L,	  0x0000, 0x0000, 0x0000, 0x0000},
 {	2, -1, -1, G_BOXCHAR,  NONE,	   NORMAL,	 (_Ob_spec_t)0x04FF1181L,	  0x0000, 0x0000, 0x0000, 0x0000},
 {	4,	3,	3, G_BOX,	   NONE,	   NORMAL,	 (_Ob_spec_t)0x00FF1191L,	  0x0000, 0x0000, 0x0000, 0x0000},
 {	2, -1, -1, G_BUTTON,   NONE,	   NORMAL,	 (_Ob_spec_t)NULL,			  0x0000, 0x0000, 0x0000, 0x0000},
 {	0, -1, -1, G_BOXCHAR,  LASTOB,	   NORMAL,	 (_Ob_spec_t)0x03FF1181L,	  0x0000, 0x0000, 0x0000, 0x0000},
};

#define NSLIDE_ROOT 		0
#define NSLIDE_DOWNARROW	1
#define NSLIDE_PAGE 		2
#define NSLIDE_SLIDER		3
#define NSLIDE_UPARROW		4

#define NSLIDE_ARROW_WIDTH		(gl_wbox)
#define MIN_NSLIDE_OBJECT_WIDTH (4*NSLIDE_ARROW_WIDTH)

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

typedef struct nslide_info {
	XUSERBLK xub;					/* standard XUSERDEF header 							 */
	short	 statusobj; 			/* obj in main tree that shows cur selection		  */
	long	 min;					/* minimum numeric value slider can have					*/
	long	 max;					/* maximum numeric value slider can have					*/
	short	 wpage; 				/* width of pager bar in pixels 						 */
	short	 wslider;				/* width of slider box in pixels						*/
	OBJECT	 dialog[5]; 			/* rectangles describing slider 					 */
} NSLIDEINFO;

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

#ifdef GEMFAST_PROTOS

#define __saveds
  typedef void GCALLBACK (EVNREPEATFUNC)(void *udata, short mx, short my);
#undef __saveds

  INTERNAL_VFUNC		clip_slider_value(NSLIDEINFO *pinfo);
  INTERNAL_VFUNC		render_nslide(NSLIDEINFO *pinfo, short startobj, GRECT *cliprect);
  static long GCALLBACK see_nslide(XPARMBLK *pb);
  static void GCALLBACK inc_slider(void *udata, short mx, short my);
  static void GCALLBACK dec_slider(void *udata, short mx, short my);
  static void			repeat_on_button_down(EVNREPEATFUNC *pfunc, void *udata);
  static void GCALLBACK udslider(OBJECT *ptree, short obj, short slidepos, void *udata);
  static XUBT_STATUS GCALLBACK feel_nslide(XUSERBLK *xub, short mx, short my, short clicks);

#else

  typedef void GCALLBACK (EVNREPEATFUNC)();

#endif

INTERNAL_VFUNC clip_slider_value(pinfo)
	NSLIDEINFO *pinfo;
/*****************************************************************************
 *
 ****************************************************************************/
{
	long	value = (long)pinfo->xub.ob_spec;

	if (value < pinfo->min) {
		value = pinfo->min;
	} else if (value > pinfo->max) {
		value = pinfo->max;
	}

	pinfo->xub.ob_spec = (_Ob_spec_t)value;
}

INTERNAL_VFUNC render_nslide(pinfo, startobj, cliprect)
	NSLIDEINFO	*pinfo;
	short		startobj;
	GRECT		*cliprect;
/*****************************************************************************
 *
 ****************************************************************************/
{
	long	value;
	long	vrange;
	short	prange;
	short	nchars;
	char	buf[20];
	OBJECT	*dialog = pinfo->dialog;

	clip_slider_value(pinfo);

	nchars = sprintf(buf, "%ld", pinfo->xub.ob_spec);
	dialog[NSLIDE_SLIDER].ob_width = (nchars+1) * gl_wchar;
	dialog[NSLIDE_SLIDER].ob_spec  = (_Ob_spec_t)buf;

	value = (long)pinfo->xub.ob_spec - pinfo->min;
	vrange = pinfo->max - pinfo->min;
	prange = dialog[NSLIDE_PAGE].ob_width - dialog[NSLIDE_SLIDER].ob_width;

	dialog[NSLIDE_SLIDER].ob_x =
		(short)((((value*1000L)/vrange)*(prange))/1000L);

	xob_draw(dialog, startobj, MAX_DEPTH, cliprect);
}

static long GCALLBACK see_nslide(pb)
	XPARMBLK *pb;
/*****************************************************************************
 *
 ****************************************************************************/
{
	NSLIDEINFO *pinfo = (NSLIDEINFO *)pb->pub;

	pinfo->dialog[ROOT].ob_x = pb->drawrect.g_x;
	pinfo->dialog[ROOT].ob_y = pb->drawrect.g_y;

	render_nslide(pinfo, ROOT, &pb->cliprect);

	return 0;
}

static void GCALLBACK inc_slider(udata, mx, my)
	void	*udata;
	short	mx;
	short	my;
/*****************************************************************************
 *
 ****************************************************************************/
{
	NSLIDEINFO *pinfo = udata;

	pinfo->xub.ob_spec = (_Ob_spec_t)((long)pinfo->xub.ob_spec + 1);
	render_nslide(pinfo, NSLIDE_PAGE, &gl_rwdesk);
}

static void GCALLBACK dec_slider(udata, mx, my)
	void	*udata;
	short	mx;
	short	my;
/*****************************************************************************
 *
 ****************************************************************************/
{
	NSLIDEINFO *pinfo = udata;

	pinfo->xub.ob_spec = (_Ob_spec_t)((long)pinfo->xub.ob_spec - 1);
	render_nslide(pinfo, NSLIDE_PAGE, &gl_rwdesk);
}

static void repeat_on_button_down(pfunc, udata)
	EVNREPEATFUNC	*pfunc;
	void			*udata;
/*****************************************************************************
 *
 ****************************************************************************/
{
	short mx, my, mb, dmy;
	short first_time = TRUE;

	do	{
		graf_mkstate(&mx, &my, &mb, &dmy);
		if (mb || first_time) {
			(*pfunc)(udata, mx, my);
		}
		first_time = 0;
		evnx_timer(50L);
	} while (mb);
}

static void GCALLBACK udslider(ptree, obj, slidepos, udata)
	OBJECT	*ptree;
	short	obj;
	short	slidepos;
	void	*udata;
/*****************************************************************************
 *
 ****************************************************************************/
{
	NSLIDEINFO *pinfo = udata;
	long		value;
	long		vrange;

	if (slidepos > 2000) {
		slidepos -= 2000;
	} else if (slidepos > 1000) {
		slidepos -= 1000;
	}

	vrange = pinfo->max - pinfo->min;

	value = (vrange * (slidepos-1)) / 1000L;
	pinfo->xub.ob_spec = (_Ob_spec_t)(value + pinfo->min);

	render_nslide(pinfo, NSLIDE_PAGE, &gl_rwdesk);
}

static XUBT_STATUS GCALLBACK feel_nslide(xub, mx, my, clicks)
	XUSERBLK *xub;
	short	 mx;
	short	 my;
	short	 clicks;
/*****************************************************************************
 * routine to handle a click on a slider object.
 ****************************************************************************/
{
	NSLIDEINFO *pinfo = (NSLIDEINFO *)xub;
	short		clickobj;
	XUBT_STATUS rv = XUBT_VALUE;

	clickobj = objc_find(pinfo->dialog, ROOT, MAX_DEPTH, mx, my);
	switch (clickobj) {
		case NSLIDE_PAGE:
		case NSLIDE_SLIDER:
			grf_udslidebox(pinfo->dialog, NSLIDE_SLIDER, FALSE, udslider, pinfo);
			break;
		case NSLIDE_DOWNARROW:
			repeat_on_button_down(dec_slider, pinfo);
			break;
		case NSLIDE_UPARROW:
			repeat_on_button_down(inc_slider, pinfo);
			break;
		default:
			rv = XUBT_NONE;
			break;
	}

	return rv;
}

short xob_nslide_create(ptree, obj, min, max)
	OBJECT *ptree;
	short	obj;
	long	min;
	long	max;
/*****************************************************************************
 *
 ****************************************************************************/
{
	register OBJECT 	 *pobj = &ptree[obj];
	register NSLIDEINFO *pinfo;

	/*------------------------------------------------------------------------
	 * validate parms.
	 *----------------------------------------------------------------------*/

	if (0 == apl_vshared()) {
		return gfErr_vdi_handle;
	}

	if (pobj->ob_width < MIN_NSLIDE_OBJECT_WIDTH) {
		return gfErr_object_too_small;
	}

	if (max <= min) {
		return gfErr_parameter_range;
	}

	/*------------------------------------------------------------------------
	 * If the object has already been made into a G_NSLIDE extended object,
	 * just get its pointer, else make it into such an object.
	 *----------------------------------------------------------------------*/

	if (NULL != (pinfo = XUBPTR(pobj))) {
		if (pinfo->xub.ob_type != G_NSLIDE) {
			return gfErr_wrong_type;
		}
	} else {
		if (NULL == (pinfo = apl_malloc((long)sizeof(*pinfo)))) {
			return gfErr_no_memory;
		}
		xob_transform(ptree, obj, &pinfo->xub, see_nslide, feel_nslide, (long)sizeof(*pinfo));
		pinfo->xub.ob_type = G_NSLIDE;
		pinfo->xub.ob_spec = 0;
	}

	pinfo->min		= min;
	pinfo->max		= max;
	pinfo->wpage	= pobj->ob_width - (2 * NSLIDE_ARROW_WIDTH);
	pinfo->wslider	= NSLIDE_ARROW_WIDTH;

	clip_slider_value(pinfo); /* in case zero is out of range! */

	/*------------------------------------------------------------------------
	 * build the rectangles describing the on-screen nslide.
	 *----------------------------------------------------------------------*/

	memcpy(pinfo->dialog, nslider_objects, sizeof(nslider_objects));

#define SetRect(idx, x, y, w, h)\
	pinfo->dialog[(idx)].ob_x = (x);\
	pinfo->dialog[(idx)].ob_y = (y);\
	pinfo->dialog[(idx)].ob_width = (w);\
	pinfo->dialog[(idx)].ob_height = (h)

	SetRect(ROOT,
		pobj->ob_x,
		pobj->ob_y,
		pobj->ob_width,
		pobj->ob_height);

	SetRect(NSLIDE_DOWNARROW,
		0,
		0,
		NSLIDE_ARROW_WIDTH,
		pobj->ob_height);

	SetRect(NSLIDE_PAGE,
		NSLIDE_ARROW_WIDTH,
		0,
		pinfo->wpage - 1,
		pobj->ob_height);

	SetRect(NSLIDE_SLIDER,
		0,
		0,
		pinfo->wslider,
		pobj->ob_height);

	SetRect(NSLIDE_UPARROW,
		NSLIDE_ARROW_WIDTH + pinfo->wpage,
		0,
		NSLIDE_ARROW_WIDTH,
		pobj->ob_height);

#undef SetRect

	return 0;
}

long xob_nslide_get(ptree, obj)
	OBJECT *ptree;
	short	obj;
/*****************************************************************************
 *
 ****************************************************************************/
{
	NSLIDEINFO *pinfo;

	if (NULL == (pinfo = XUBPTR(&ptree[obj]))) {
		return 0;
	}
	return (long)pinfo->xub.ob_spec;
}

void xob_nslide_set(ptree, obj, newvalue)
	OBJECT *ptree;
	short	obj;
	long	newvalue;
/*****************************************************************************
 *
 ****************************************************************************/
{
	NSLIDEINFO *pinfo;

	if (NULL == (pinfo = XUBPTR(&ptree[obj]))) {
		return;
	}
	pinfo->xub.ob_spec = (_Ob_spec_t)newvalue;
	clip_slider_value(pinfo);
}

void xob_nslide_change(ptree, obj, newmin, newmax)
	OBJECT *ptree;
	short	obj;
	long	newmin;
	long	newmax;
/*****************************************************************************
 *
 ****************************************************************************/
{
	NSLIDEINFO *pinfo;

	if (NULL == (pinfo = XUBPTR(&ptree[obj]))) {
		return;
	}

	pinfo->min = newmin;
	pinfo->max = newmax;

	clip_slider_value(pinfo);
}

