/**************************************************************************
 *
 *************************************************************************/

#include "gemfintl.h"

/*----------------------------------------------------------------------------
 *
 *--------------------------------------------------------------------------*/

static XUBT_STATUS maybe_touch_object  __PROTO((OBJECT *ptree, short obj));
static short	   call_system_form_do __PROTO((FormControl *ctl));
static short	   find_mover_object   __PROTO((OBJECT *ptree));

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

#define NO_FLAGS		(-1)
#define BLITOPTIONS 	(FRM_USEBLIT|FRM_MOVEABLE)
#define CTL_ALLOCATED	0x80000000L

long  _FrmDefaults	= FRM_NORMAL;

long		 (*_FrBltVector) __PROTO((short options, void *buffer, void *rect));
static short (*default_form_do) __PROTO((FormControl *ctl)) = call_system_form_do;

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

static short call_system_form_do(ctl)
	register FormControl *ctl;
{
	return form_do(ctl->ptree, ctl->editobj);
}


/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

static XUBT_STATUS maybe_touch_object(ptree, obj)
	OBJECT *ptree;
	short	obj;
{
	OBJECT		*pobj;
	XUSERBLK	*xub;
	short		 clicks;
	short		 mx;
	short		 my;
	short		 dmy;

	clicks = (obj & 0x8000) ? 2 : 1;
	obj &= 0x7FFF;
	pobj = &ptree[obj];

	if (pobj->ob_flags & INDIRECT) {
		xub = *(XUSERBLK **)pobj->ob_spec;
	} else {
		xub = (XUSERBLK *)pobj->ob_spec;
	}

	if ((pobj->ob_type & 0x00FF) == G_USERDEF &&
			(xub->ub_self == xub) &&
			(xub->ub_touch != NULL)) {
		graf_mkstate(&mx, &my, &dmy, &dmy);
		return (*xub->ub_touch)(xub, mx, my, clicks);
	}

	return XUBT_NONE;

}

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

static short find_mover_object(ptree)
	register OBJECT *ptree;
{
	register OBJECT *pobj	  = ptree;
	register short	   obj		= 0;
	register short	   ob_flags;

	for (;;) {
		ob_flags = pobj->ob_flags;
		if (ob_flags & FRM_MOVER) {
			return obj;
		}
		if (ob_flags & LASTOB) {
			return NO_OBJECT;
		}
		++obj;
		++pobj;
	}
}

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

FormControl *_FrmSetup(ctl, options, ptree, pboundrect)
	register FormControl *ctl;
	register long		 options;
	register OBJECT 	 *ptree;
	register GRECT		 *pboundrect;
{
	if (pboundrect == NULL) {
		pboundrect = &gl_rwdesk;
	}

	if (!(options & FRM_NODEFAULTS)) {
		options |= _FrmDefaults;
	}

	ctl->ptree		  = ptree;
	ctl->pboundrect   = pboundrect;
	ctl->options	  = options & FRM_OPTIONBITS;
	ctl->form_do	  = default_form_do;
	ctl->editobj	  = ROOT;
	ctl->parentobj	  = ROOT;
	ctl->moverobj	  = NO_OBJECT;
	ctl->treeflags	  = NO_FLAGS;
	ctl->select_state = SELECTED;
	ctl->blitbuffer   = NULL;

	if (options & FRM_DSTART) {
		if (options & FRM_NEARMOUSE) {
			short mx, my, dmy;
			graf_mkstate(&mx, &my, &dmy, &dmy);
			ptree->ob_x = mx - (ptree->ob_width  / 2);
			ptree->ob_y = my - (ptree->ob_height / 2);
			frm_confine(ptree, pboundrect);
		} else if (options & FRM_CENTER) {
			frmx_center(ptree, &ctl->scrnrect);
		}
	}

	frm_sizes(ptree, &ctl->scrnrect);
	rc_scale(&ctl->scrnrect, &ctl->littlerect, 20);

	if (options & FRM_DSTART) {
		if ((options & BLITOPTIONS) && _FrBltVector != NULL) {
			long blitbytes;
			blitbytes = (*_FrBltVector)(GRF_BMEMCALC, NULL, &ctl->scrnrect);
			if (NULL != (ctl->blitbuffer = apl_malloc(blitbytes))) {
				ctl->moverobj = find_mover_object(ptree);
				if (ctl->moverobj == NO_OBJECT && (options & FRM_MOVEABLE)) {
					ctl->moverobj  = ROOT;
					ctl->treeflags = frm_mkmoveable(ptree, ROOT);
				}
			}
		}
	}

	return ctl;
}

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

void frm_cleanup(ctl)
	register FormControl *ctl;
{
	if (ctl != NULL) {
		if (ctl->blitbuffer != NULL) {
			apl_free(ctl->blitbuffer);
		}
		if (ctl->ptree != NULL && ctl->treeflags != NO_FLAGS) {
			ctl->ptree->ob_flags = ctl->treeflags;
		}
		if (ctl->options & CTL_ALLOCATED) {
			apl_free(ctl);
		}
	}
}

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

FormControl *frm_init(options, ptree, pboundrect)
	long		options;
	OBJECT		*ptree;
	GRECT		*pboundrect;
{
	FormControl *ctl;

	if (NULL == (ctl = apl_malloc((long)sizeof(FormControl)))) {
		return NULL;
	} else {
		_FrmSetup(ctl, options|FRM_DSTART, ptree, pboundrect);
		ctl->options |= CTL_ALLOCATED;
		return ctl;
	}
}

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

void frm_start(ctl)
	register FormControl *ctl;
{
	if (ctl->blitbuffer != NULL) {
		(*_FrBltVector)(GRF_BFROMSCREEN, ctl->blitbuffer, &ctl->scrnrect);
	}

	frmx_dial(FMD_START, &ctl->littlerect, &ctl->scrnrect);

	if (ctl->options & FRM_EXPLODE) {
		frmx_dial(FMD_GROW,  &ctl->littlerect, &ctl->scrnrect);
	}

}

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

void frm_draw(ctl, obj)
	register FormControl *ctl;
	short				   obj;
{
	objc_draw(ctl->ptree, obj, MAX_DEPTH, RECTVALS(ctl->pboundrect));
}

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

void frm_finish(ctl)
	register FormControl *ctl;
{
	if (ctl->options & FRM_EXPLODE) {
		frmx_dial(FMD_SHRINK, &ctl->littlerect, &ctl->scrnrect);
	}

	if (ctl->blitbuffer != NULL) {
		(*_FrBltVector)(GRF_BTOSCREEN, ctl->blitbuffer, &ctl->scrnrect);
	} else {
		frmx_dial(FMD_FINISH, &ctl->littlerect, &ctl->scrnrect);
		evnx_timer(1L);
	}

}

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

void frm_move(ctl)
	register FormControl *ctl;
{
	short			 oldmouse;
	short			 mb;
	short			 dmy;
	register short	 adjust;
	register GRECT *prect = (GRECT *)&ctl->ptree->ob_x;

	if (ctl->blitbuffer == NULL) {
		return;
	}

	/*
	 * delay very briefly, then see if the mouse button is still down.
	 * if it's not, just return.  this prevents false 'moves' when all
	 * the user did was miss the button s/he was after.
	 */

	evnx_timer(75L);
	graf_mkstate(&dmy, &dmy, &mb, &dmy);
	if (!mb) {
		return;
	}

	(*_FrBltVector)(GRF_BTOSCREEN, ctl->blitbuffer, &ctl->scrnrect);

	adjust = obj_clcalc(ctl->ptree, ROOT, NULL, NULL);
	rc_gadjust(prect, adjust, adjust);

	oldmouse = graf_mouse(FLAT_HAND, NULL);
	grfx_dragbox(prect, ctl->pboundrect, prect);
	graf_mouse(oldmouse, NULL);

	adjust = -adjust;
	rc_gadjust(prect, adjust, adjust);

	frm_sizes(ctl->ptree, &ctl->scrnrect);
	(*_FrBltVector)(GRF_BFROMSCREEN, ctl->blitbuffer, &ctl->scrnrect);
	frm_draw(ctl, ROOT);
}

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

short frm_do(ctl, editobj)
	register FormControl *ctl;
	short				   editobj;
{
	short oldmouse;
	short more_interaction;
	short rv;
	short obj;
	XUBT_STATUS status;

	if (ctl->options & FRM_MOUSEARROW) {
		oldmouse = graf_mouse(ARROW, 0L);
	}

	ctl->editobj = editobj;

	do	{
		more_interaction = FALSE;
		obj = 0x7FFF & (rv = (*ctl->form_do)(ctl));
		if (rv == NO_OBJECT) {
			goto QUICK_EXIT;
		}
		if (obj == ctl->moverobj) {
			more_interaction = TRUE;
			frm_move(ctl);
		}
		if (ctl->ptree[obj].ob_flags & TOUCHEXIT) {
			if (ctl->ptree[obj].ob_state & DISABLED) {
				more_interaction = TRUE;
			} else {
				if ((ctl->ptree[obj].ob_type & 0x00FF) == G_USERDEF) {
					more_interaction = TRUE;
					status = maybe_touch_object(ctl->ptree, rv);
					if (status & XUBT_DCEXIT) {
						if (NO_OBJECT != (rv = obj = obj_dxfind(ctl->ptree))) {
							more_interaction = FALSE;
						}
					}
				}
			}
		}
	} while (more_interaction);

	if (ctl->ptree[obj].ob_flags & (EXIT|DEFAULT)) {
		evnx_timer(70L);
		obj_stchange(ctl->ptree, obj, ~ctl->select_state,
			OBJ_CLIPDRAW, ctl->pboundrect);
	}

QUICK_EXIT:

	if (ctl->options & FRM_MOUSEARROW) {
		graf_mouse(oldmouse, 0L);
	}

	return rv;
}

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

short frm_dialog(options, ptree, object)
	long	options;
	OBJECT *ptree;
	short	  object;
{
	FormControl ctl;
	short		  selobj;
	long		actions;

	if (0 == (actions = options & FRM_ACTIONBITS)) {
		options  |= (actions = FRM_DCOMPLETE);
	}

	if (actions == FRM_DCOMPLETE) {
		wind_update(BEG_UPDATE);
	} else {
		if (options & BLITOPTIONS) {
			options &= ~BLITOPTIONS;
		}
	}

	_FrmSetup(&ctl, options, ptree, NULL);

	if (actions & FRM_DSTART) {
		frm_start(&ctl);
	}

	if (actions & FRM_DDRAW) {
		frm_draw(&ctl, (options & FRM_DSTART) ? ROOT : object);
	}

	if (actions & FRM_DDO) {
		selobj = frm_do(&ctl, object);
	}

	if (actions & FRM_DFINISH) {
		frm_finish(&ctl);
	}

	frm_cleanup(&ctl);

	if (actions == FRM_DCOMPLETE) {
		wind_update(END_UPDATE);
	}

	return selobj;
}

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

long frm_defaults(options)
	long options;
{
	long rv = _FrmDefaults;

	if (options != FRM_GETDEFAULTS) {
		_FrmDefaults = options & FRM_OPTIONBITS & ~FRM_NODEFAULTS;
	}
	return rv;
}

/*-------------------------------------------------------------------------
 *
 *-----------------------------------------------------------------------*/

void *frm_dovector(newfunc)
	void *newfunc;
{
	void *rv = default_form_do;

	if (newfunc != NULL) {
		default_form_do = newfunc;
	}
	return rv;
}



