/* ------------------------------------------------------------------------- */
/* ----- Let 'em Fly!   V 1.2 ----------- (c) 1991-93 by Oliver Scheel ----- */
/* ------------------------------------------------------------------------- */
/* ----- Module: ltmf_do.c   new form_do() --------------------------------- */
/* ------------------------------------------------------------------------- */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <tos.h>
#include <smallaes.h>
#include <vdi.h>
#include <magx_old.h>
#include <portab.h>

#include "letemfly.h"
#include "ltmf_str.h"
#include "ltmf_def.h"

/* ------------------------------------------------------------------------- */

EXTERN	WORD	global[];

EXTERN	WORD	vwk_handle,
		colors,
		sysfont;

EXTERN	WORD	gl_wchar,
		gl_hchar;
EXTERN	GRECT	wi_max;
EXTERN	WORD	ind_mode,
		act_mode;

EXTERN	OBJECT	*lasttree;

EXTERN	OBJECT	*info_dial,
		*char_dial;

EXTERN	WORD	magic;
EXTERN	WORD	isalert;
EXTERN	WORD	initflag;
EXTERN	WORD	jacks_in;
EXTERN	WORD	was_used;

EXTERN	MAGX_COOKIE	*magx;

/* ------------------------------------------------------------------------- */

WORD vwk_init	_((VOID));
VOID vwk_exit	_((WORD handle));
VOID obj_xywh	_((OBJECT *tree, WORD obj, GRECT *p));
VOID obj_update	_((OBJECT *tree, WORD obj));
VOID STDARGS obj_clsize	_((OBJECT *tree, WORD obj, WORD *x, WORD *y, WORD *w, WORD *h));
VOID rc_gtov	_((GRECT *src, VRECT *dest));
VOID rc_vtog	_((VRECT *src, GRECT *dest));
WORD ltmf_init	_((VOID));

WORD nfm_do	_((OBJECT *tree, WORD startobj));
WORD nfm_dial	_((WORD flag, WORD xs, WORD ys, WORD ws, WORD hs, WORD x, WORD y, WORD w, WORD h));
WORD nfm_center	_((OBJECT *tree, WORD *x, WORD *y, WORD *w, WORD *h));
WORD nfm_button	_((OBJECT *tree, WORD obj, WORD clicks, WORD *nxtobj));
WORD nfm_keybd	_((OBJECT *tree, WORD obj, WORD next_obj, WORD thechar, WORD *pnxt_obj, WORD *pchar));
WORD nob_edit	_((OBJECT *tree, WORD obj, WORD thechar, WORD *idx, WORD kind));
VOID STDARGS di_fly	_((OBJECT *tree));
WORD STDARGS di_moveto	_((OBJECT *tree, WORD x, WORD y));
WORD STDARGS di_center	_((OBJECT *tree));

CHAR STDARGS ins_spcchar	_((VOID));
WORD STDARGS lookup_key	_((WORD key, WORD status));
WORD STDARGS init_keys	_((OBJECT *tree ));

MLOCAL WORD _conv_ch	_((CHAR ch));
MLOCAL VOID _dialval	_((WORD mode));
MLOCAL CHAR *_get_text	_((OBJECT *tree, WORD obj, CHAR **txtptr, WORD *tlen));
MLOCAL WORD _is_spec	_((ALIAS *list, WORD num, CHAR *text));
MLOCAL WORD _inskeytab	_((WORD pos , WORD obj , WORD keyobj , WORD cpos , WORD tlen ));
MLOCAL VOID _ins_key	_((OBJECT *tree, WORD obj, WORD in));
MLOCAL VOID _und_key	_((OBJECT *tree, KBDTAB *ktab, WORD obj, WORD mode));
MLOCAL WORD _find_kobj	_((OBJECT *tree, WORD obj));
MLOCAL WORD _getcpos	_((OBJECT *tree, WORD obj, WORD x));
MLOCAL VOID _tw		_((OBJECT *tree, WORD start));
MLOCAL WORD _init_fm	_((OBJECT *tree, WORD startobj));

VOID _und_obj	_((OBJECT *tree, KBDTAB *ktab, WORD obj, WORD mode));

/* ------------------------------------------------------------------------- */

KBDTAB	keytable[MAXKEYS],
	oldkbtab[MAXKEYS];

WORD	undo_obj;
WORD	help_obj;
WORD	fly_obj;

ALIAS	undos[MAXUNDO] = {	"abbruch",	7,
				"abbrechen",	9,
				"verlassen",	9,
				"nein",		4,
				"ende",		4,
				"quit",		4,
				"cancel",	6,
				"abort",	5,
				"no",		2,
				"exit",		4 };

ALIAS	helps[MAXHELP] = {	"help",		4,
				"hilfe",	5 };

/*
ALIAS	oks[MAXOK] = {		"ok",		2,
				"ja",		2,
				"yes",		3 };
*/

CHAR	txtbuf[2];		/* small text buffer	*/

WORD	keys;			/* key dials available	*/
WORD	in_do = 0;		/* form_do ist active	*/
WORD	ed_flag = 0;		/* no EDITABLE		*/

OBJECT	*lastdone = NULL;

/* ------------------------------------------------------------------------- */
/* ----- utilities --------------------------------------------------------- */
/* ------------------------------------------------------------------------- */

/* ----- convert char to pos ----------------------------------------------- */

MLOCAL WORD _conv_ch(ch)
CHAR	ch;
{
	ch = toupper(ch);
	if(isdigit(ch))
		ch -= 48;
	else if(isalpha(ch))
		ch -= 55;
	else
		ch = -1;
	return(ch);
}

/* ----- save/restore dialog values ---------------------------------------- */

MLOCAL VOID _dialval(mode)
WORD	mode;
{
	static WORD	ksav, jsav, fsav, wusav;
	static OBJECT	*tsav;
/*	static MFORM	mf_sav;
*/
	if(mode == DSAVE)
	{
		ksav = keys;		/* save special values */
		fsav = fly_obj;
		tsav = lastdone;
		jsav = jacks_in;
		wusav = was_used;
		jacks_in = 1;
/*		memcpy(&mf_sav, &mf, sizeof(MFORM));
*/	}
	else
	{
		keys = ksav;		/* restore values */
		fly_obj = fsav;
		lastdone = tsav;
		jacks_in = jsav;
		was_used = wusav;
/*		memcpy(&mf, &mf_sav, sizeof(MFORM));
*/	}
}

/* ----- insert special character ------------------------------------------ */

CHAR STDARGS ins_spcchar()
{
	WORD	x, y, w, h;
	WORD	choice, d;
	CHAR	ch;
	GRECT	gr;

	isalert = TRUE;
	_dialval(DSAVE);
	nfm_center(char_dial, &x, &y, &w, &h);
	nfm_dial(FMD_START, 0, 0, 0, 0, x, y, w, h);
	objc_draw(char_dial, ROOT, MAX_DEPTH, x, y, w, h);
	choice = nfm_do(char_dial, 0) & 0x7fff;
	nfm_dial(FMD_FINISH, 0, 0, 0, 0, x, y, w, h);
	char_dial[choice].ob_state &= ~SELECTED;
	_dialval(DRESTORE);
	isalert = FALSE;
	if(choice != CHARCANC)
	{
		graf_mkstate(&x, &y, &d, &d);
		evnt_button(1, 1, 0, &d, &d, &d, &d);
		obj_xywh(char_dial, choice, &gr);
		x = (x - gr.g_x) / gl_wchar;
		choice = (choice - CHARTXT0) * 32 + x;
		if(choice == 0)
			ch = 127;
		else if(choice < 32)
			ch = choice;
		else
			ch = choice - 160;
		return(ch);
	}
	return(0);
}

/* ----- get text from object ---------------------------------------------- */

MLOCAL CHAR *_get_text(tree, obj, txtptr, tlen)
OBJECT	*tree;
WORD	obj;
CHAR	**txtptr;
WORD	*tlen;
{
	WORD	typ;
	CHAR	*txtp;
	OBJECT	*tr;
	OBSPEC	*obspc;

	tr = &tree[obj];
	typ = tr->ob_type & 0xff;
	if(magic && (typ == G_USERDEF) && (tr->ob_type & EXAPPL))
	{
		typ = tr->ob_spec.exapblk->ub_type & 0xff;
		obspc = &tr->ob_spec.exapblk->ub_spec;
	}
	else
		obspc = (tr->ob_flags & INDIRECT) ? &tr->ob_spec.indirect : &tr->ob_spec;
	if((typ == G_TEXT) || (typ == G_BOXTEXT))
		txtp = obspc->tedinfo->te_ptext;
	else if((typ == G_FTEXT) || (typ == G_FBOXTEXT))
		txtp = obspc->tedinfo->te_ptmplt;
	else if(typ == G_BOXCHAR)
	{
		txtbuf[0] = obspc->obspec.character;
		txtbuf[1] = 0;
		txtp = txtbuf;
	}
	else if((typ == G_BUTTON) || (typ == G_STRING))
		txtp = obspc->free_string;
	else
		txtp = strcpy(txtbuf, "");
	if(tlen != NULL)
		*tlen = (WORD)strlen(txtp);
	if(txtptr != NULL)
	{
		*txtptr = txtp;
		while(**txtptr && !isalnum(**txtptr))
			(*txtptr)++;
	}
	return(txtp);
}

MLOCAL WORD _is_spec(list, num, text)
ALIAS	*list;
WORD	num;
CHAR	*text;
{
	REG WORD	i;

	for(i = 0; (i < num) && list[i].text; i++)
	{
		if(!strnicmp(text, list[i].text, list[i].len))
			return(TRUE);
	}
	return(FALSE);
}

MLOCAL WORD _inskeytab(pos, obj, keyobj, cpos, tlen)
WORD	pos,
	obj,
	keyobj,
	cpos,
	tlen;
{
	KBDTAB	*kt;
	WORD	savobj;

	kt = &keytable[pos];
	savobj = kt->obj;
	kt->obj = obj;
	kt->kobj = keyobj;
	kt->chpos = cpos;
	kt->tlen = tlen;
	return(savobj);
}

/* ----- insert key into table --------------------------------------------- */

MLOCAL VOID _ins_key(tree, obj, in)
OBJECT	*tree;
WORD	obj,
	in;
{
	REG WORD	i;
	WORD	pos,
		extype;
	WORD	savobj,
		keyobj,
		priv,
		tlen,
		cpos;
	CHAR	*txtp,
		*txtp2;
	KBDTAB	*kt;
	OBJECT	*tr, *tr1;

	savobj = 0;
	keyobj = obj;
	priv = FALSE;
	extype = tree[keyobj].ob_type & 0xff00;
	cpos = ((extype & 0x0f00) >> 8) - 1;
	if(magic && (extype & TKNEXT))
		keyobj++;
	if(magic && (extype & (0x0f00|SPECIAL)) && in)
	{
		if(extype & SPECIAL)
		{
			switch(cpos)
			{
			    case 0    :	undo_obj = obj;
					_ins_key(tree, obj, FALSE);
			    		break;
			    case 1    :	help_obj = obj;
					_ins_key(tree, obj, FALSE);
			    		break;
			    default   : kt = &keytable[cpos+MAXALPH-2];
			    		kt->obj = obj;
			    		kt->kobj = 0;
			    		goto norm_ins;
			}
		}
		else
		{
			txtp = _get_text(tree, keyobj, &txtp2, &tlen);
			pos = _conv_ch(txtp[cpos]);
			if(pos == -1)
				goto leave;
			savobj = _inskeytab(pos, obj, keyobj, cpos, tlen);
			if(savobj)
				_ins_key(tree, savobj, FALSE);
		}
	}
	else
	{
norm_ins:	txtp = _get_text(tree, keyobj, &txtp2, &tlen);
		tr = &tree[obj];
		if(magic && (tr->ob_flags & NODUPE))
			goto leave;
		if((tlen == 0) && !(tr->ob_flags & LASTOB))
		{
			tr1 = &tree[obj+1]; /* tr + sizeof(OBJECT); */
			if(!(tr1->ob_flags & (SELECTABLE|TOUCHEXIT|EXIT|HIDETREE)) && !(tr1->ob_state & DISABLED))
			{
				if(abs(tr1->ob_y - tr->ob_y) < 3)
				{
					keyobj = obj + 1;
					txtp = _get_text(tree, keyobj, &txtp2, &tlen);
				}
			}
		}
		if(tr->ob_flags & DEFAULT)
			priv = TRUE;
		if(tlen)
		{
			if(in)
			{
				if(!(magic && (undo_obj ? (tree[undo_obj].ob_type & SPECIAL) : FALSE)))
				{
					if(_is_spec(undos, MAXUNDO, txtp2))
					{
						priv = TRUE;
						undo_obj = obj;
					}
				}
				if(_is_spec(helps, MAXHELP, txtp2))
					help_obj = obj;
/*				if(_is_spec(oks, MAXOK, txtp2))
					priv = TRUE;
*/				if(priv)	/* take 1st letter */
				{
					pos = _conv_ch(txtp2[0]);
					savobj = _inskeytab(pos, obj, keyobj, (WORD)(&txtp2[0] - txtp), tlen);
					if(savobj)
						_ins_key(tree, savobj, FALSE);
					goto leave;
				}
				if((txtp[0] >= '\x01') && (txtp[0] <= '\x04'))
				{
					kt = &keytable[MAXALPH+MAXFKEY+txtp[0]-0x01];
			    		kt->obj = obj;
			    		kt->kobj = 0;
			    		goto leave;
				}
			}
			i = 0;
			while(txtp2[i])		/* check words */
			{
				if(isalnum(txtp2[i]))
				{
					pos = _conv_ch(txtp2[i]);
					if(pos != -1)
					{
						if(!keytable[pos].obj)
						{
							_inskeytab(pos, obj, keyobj, (WORD)(&txtp2[i] - txtp), tlen);
							goto leave;
						}
					}
				}
				while(isalnum(txtp2[i]))
					i++;
				while(txtp2[i] && !isalnum(txtp2[i]))
					i++;
			}
			for(i = 0; txtp2[i]; i++)
			{
				pos = _conv_ch(txtp2[i]);
				if(pos != -1)
				{
					if(!keytable[pos].obj)
					{
						_inskeytab(pos, obj, keyobj, (WORD)(&txtp2[i] - txtp), tlen);
						goto leave;
					}
				}
			}
		}
leave:	;
	}
}

/* ----- underline key object ---------------------------------------------- */

MLOCAL VOID _und_key(tree, ktab, obj, mode)
OBJECT	*tree;
KBDTAB	*ktab;
WORD	obj,
	mode;
{
	REG WORD	kobj,
			typ,
			just;
	WORD	wchar, hchar;
	WORD	bcol, ucol, patt, fsize;
	WORD	isuser;
	WORD	d;
	CHAR	ch[2];
	CHAR	*tp;
	GRECT	gr;
	VRECT	vr;
	KBDTAB	*kt;
	OBJECT	*tr;
	OBSPEC	*obspc;

	if(obj < MAXALPH)
	{
		isuser = FALSE;
		kt = &ktab[obj];
		kobj = kt->kobj;
		tr = &tree[kobj];
		typ = tr->ob_type & 0xff;
		if(magic && (typ == G_USERDEF) && (tr->ob_type & EXAPPL))
		{
			typ = tr->ob_spec.exapblk->ub_type & 0xff;
			obspc = &tr->ob_spec.exapblk->ub_spec;
			isuser = TRUE;
		}
		else
			obspc = (tr->ob_flags & INDIRECT) ? &tr->ob_spec.indirect : &tr->ob_spec;
		if(kt->obj)
		{
			obj_xywh(tree, kobj, &gr);
			wchar = gl_wchar;
			hchar = gl_hchar;
			if((typ == G_BUTTON) || (typ == G_BOXCHAR))
				just = isuser ? TE_RIGHT : TE_CNTR;
			else if(typ == G_STRING)
				just = isuser ? TE_RIGHT : TE_LEFT;
			else
			{
				just = obspc->tedinfo->te_just;
				if(obspc->tedinfo->te_font == SMALL)
					wchar = hchar = 6;
			}
			switch(just)
			{
				case TE_LEFT  :	vr.v_x1 = 0;
						break;
				case TE_RIGHT :	vr.v_x1 = gr.g_w - kt->tlen * wchar;
						break;
				case TE_CNTR  :	vr.v_x1 = (gr.g_w - kt->tlen * wchar + 1) / 2;
			}
			vr.v_x1 += gr.g_x + kt->chpos * wchar;
			vr.v_y1 = gr.g_y + (gr.g_h - hchar) / 2 + hchar - 1;
			if((AESVersion() >= 0x0340) && !magx
				&& ((((tr->ob_flags & 0x0600) == 0x0600) && (act_mode == 1))
				|| (((tr->ob_flags & 0x0600) == 0x0200) && (ind_mode == 1))))
			{
				vr.v_y1--;
				vr.v_x1--;
			}
			vr.v_x2 = vr.v_x1 + wchar - 1;
			vr.v_y2 = vr.v_y1;
			switch(typ)
			{
			    case G_TEXT       :
			    case G_BOXTEXT    :
			    case G_FTEXT      :
			    case G_FBOXTEXT   :	bcol = obspc->tedinfo->te_color & 0x0f;
			    			patt = (obspc->tedinfo->te_color & 0x70) >> 4;
			    			fsize = obspc->tedinfo->te_font;
			    			break;
			    case G_BOXCHAR    : bcol = obspc->obspec.interiorcol;
			    			patt = obspc->obspec.fillpattern;
			    			fsize = IBM;
			    			break;
			    default           :	bcol = 0;
			    			patt = 0;
			    			fsize = IBM;
			    			break;
			}
			ucol = letemfly.ucol & 0xff;
			if(ucol >= colors)
				ucol = 1;
			if(bcol >= colors)
				bcol = 1;
			if((tr->ob_state & SELECTED) && !isuser)
			{
				bcol ^= 0x01;
				ucol ^= 0x01;
			}
			if((patt > 3) && (ucol == bcol))
				ucol ^= 0x01;
			if((letemfly.ucol & 0x100) && (colors > 2))
			{
				if(mode)
				{
					tp = _get_text(tree, kobj, NULL, NULL);
					ch[0] = tp[kt->chpos];
					ch[1] = '\0';
					if(fsize == IBM)
						vst_height(vwk_handle, sysfont, &d, &d, &d, &d);
					else
						vst_height(vwk_handle, 4, &d, &d, &d, &d);
					vst_color(vwk_handle, ucol);
					vst_alignment(vwk_handle, TE_LEFT, 3, &d, &d);
					vswr_mode(vwk_handle, MD_TRANS);
					vs_clip(vwk_handle, 0, (WORD *)&vr);
					v_gtext(vwk_handle, vr.v_x1, vr.v_y1, ch);
				}
				else
				{
/*					obj_xywh(tree, kobj, &gr);
*/					vr.v_y1 -= (hchar + 1);
					rc_vtog(&vr, &gr);
					objc_draw(tree, ROOT, MAX_DEPTH, gr.g_x, gr.g_y, gr.g_w, gr.g_h);
				}
			}
			else
			{
				vsl_color(vwk_handle, mode ? ucol : bcol);
				v_pline(vwk_handle, 2, &vr);
			}
		}
	}
}

VOID _und_obj(tree, ktab, obj, mode)
OBJECT	*tree;
KBDTAB	*ktab;
WORD	obj,
	mode;
{
	REG WORD	i;
	VRECT	vr;

	if((lastdone == tree) && keys)	/* && in_do */
	{
		for(i = 0; i < MAXALPH; i++)
		{
			if(keytable[i].obj == obj)
			{
				graf_mouse(M_OFF, 0l);
				vwk_handle = vwk_init();
				vswr_mode(vwk_handle, MD_REPLACE);
				rc_gtov(&wi_max, &vr);
				vs_clip(vwk_handle, 0, &vr);
				_und_key(tree, ktab, i, mode);
				vwk_exit(vwk_handle);
				graf_mouse(M_ON, 0l);
				break;
			}
		}
	}
}

/* ----- lookup key object ------------------------------------------------- */

WORD STDARGS lookup_key(key, status)
WORD	key,
	status;
{
	WORD	x, y, w, h,
		exit_obj;
	KEYTAB	*ktab;

	key = (key >> 8) & 0xff;
	if((status & 0x03) && (key == 98) && !isalert)	/* Info Key */
	{
		isalert = TRUE;
		_dialval(DSAVE);
		nfm_center(info_dial, &x, &y, &w, &h);
		nfm_dial(FMD_START, 0, 0, 0, 0, x, y, w, h);
		objc_draw(info_dial, ROOT, MAX_DEPTH, x, y, w, h);
		exit_obj = nfm_do(info_dial, 0) & 0x7fff;
		nfm_dial(FMD_FINISH, 0, 0, 0, 0, x, y, w, h);
		info_dial[exit_obj].ob_state &= ~SELECTED;
		_dialval(DRESTORE);
		isalert = FALSE;
		return(0);
	}
	if(keys)
	{
		if(key == 97)	/* UNDO Key */
			return(undo_obj);
		if(key == 98)	/* HELP Key */
			return(help_obj);
		if(!ed_flag)
		{
			switch(key)
			{
				case 72 :	/* Csr Up */
					return(keytable[MAXALPH+MAXFKEY].obj);
				case 80 :	/* Crs Down */
					return(keytable[MAXALPH+MAXFKEY+1].obj);
				case 77 :	/* Crs Right */
					return(keytable[MAXALPH+MAXFKEY+2].obj);
				case 75 :	/* Crs Left */
					return(keytable[MAXALPH+MAXFKEY+3].obj);
			}
		}
		if((key >= 59) && (key <= 68))	/* F-Key */
			return(keytable[key+MAXALPH-59].obj);
		if((status & 0x08) || !ed_flag)	/* ALT Key / Alerts */
		{
			ktab = Keytbl((VOID *)-1l, (VOID *)-1l, (VOID *)-1l);
			if((key >= 120) && (key <= 129))
				key = (key == 129) ? 0 : (key - 119);
/*			else if((key >= 1) && (key <= 11))
				key--;
*/			else
			{
				key = _conv_ch(ktab->capslock[key]);
				if(key == -1)
					return(0);
			}
			return(keytable[key].obj);
		}
	}
	return(0);
}

/* ----- find key object --------------------------------------------------- */

MLOCAL WORD _find_kobj(tree, obj)
OBJECT	*tree;
WORD	obj;
{
	WORD	i;

	if(keys && (obj != 0))
	{
		for(i = 0; i < MAXALPH; i++)
		{
			if(keytable[i].kobj == obj)
				return((tree[keytable[i].kobj].ob_flags & EDITABLE) ? obj : keytable[i].obj);
		}
	}
	return(obj);
}

/* ----- calculate position within EDITABLE objects ------------------------ */

MLOCAL WORD _getcpos(tree, obj, x)
OBJECT	*tree;
WORD	obj,
	x;
{
	REG WORD	i,
			mpos;
	WORD	slen,
		mlen,
		typ;
	CHAR	*tmask,
		*text;
	GRECT	gr;
	OBJECT	*tr;
	OBSPEC	*obspc;

	mpos = -1;
	tr = &tree[obj];
	typ = tr->ob_type;
	if(magic && (typ == G_USERDEF) && (tr->ob_type & EXAPPL))
	{
		typ = tr->ob_spec.exapblk->ub_type & 0xff;
		obspc = &tr->ob_spec.exapblk->ub_spec;
	}
	else
		obspc = (tr->ob_flags & INDIRECT) ? &tr->ob_spec.indirect : &tr->ob_spec;
	text = obspc->tedinfo->te_ptext;
	if(text[0] != '@')
	{
		tmask = obspc->tedinfo->te_ptmplt;
		slen = (WORD)strlen(text);
		mlen = (WORD)strlen(tmask) * gl_wchar;
		obj_xywh(tree, obj, &gr);
		switch(obspc->tedinfo->te_just)
		{
			case TE_RIGHT :	gr.g_x += gr.g_w - mlen;
					break;
			case TE_CNTR  :	gr.g_x += (gr.g_w - mlen) / 2;
		}
		x -= gl_wchar / 2;
		i = -1;
		do
		{
			i++;
			while((tmask[i] != '_') && tmask[i])
				i++;
			mpos++;
		}
		while(((gr.g_x + i * gl_wchar) < x) && (mpos <= slen) && tmask[i]);
		if(mpos > slen)
			mpos = slen;
	}
	if(mpos < 0)
		mpos = 0;
	return(mpos);
}

/* ----- little treewalk to scan objects ----------------------------------- */

MLOCAL VOID _tw(tree, start)
OBJECT	*tree;
WORD	start;
{
	REG WORD	obj,
			flag;
	OBJECT	*tr;

	for(obj = tree[start].ob_head; (obj != start) && (obj != -1); obj = tree[obj].ob_next)
	{
		tr = &tree[obj];
		flag = tr->ob_flags;
		if(!ed_flag && (flag & EDITABLE))
			ed_flag = TRUE;
		if((flag & (SELECTABLE|TOUCHEXIT|EXIT)) 
			&& !(flag & HIDETREE)
			&& !(magic && (flag & KEYDIAL))
			&& !(tr->ob_state & (DISABLED|0xff00))
			&& ((tr->ob_type & 0xff00) != GLOBOFF))
			_ins_key(tree, obj, TRUE);
		if(!(flag & HIDETREE) && !(magic && (flag & KEYDIAL)))
			_tw(tree, obj);
	}
}

/* ----- initilialize keys ------------------------------------------------- */

WORD STDARGS init_keys(tree)
OBJECT	*tree;
{
	REG WORD	i;
	VRECT	vr;

	if(!initflag)
		ltmf_init();
	magic = ((UWORD)(tree[ROOT].ob_type & 0xff00) == MAGIC) ? TRUE : FALSE;
	keys = ((letemfly.conf & C_KEYS) && !(magic && (tree[ROOT].ob_flags & KEYDIAL))) ? TRUE : FALSE;
	ed_flag = FALSE;
	if(tree != lastdone)
	{
		for(i = 0; i < MAXKEYS; i++)
		{
			oldkbtab[i].obj = 0;
			oldkbtab[i].kobj = 0;
		}
	}
	if(keys)
	{
		for(i = 0; i < MAXKEYS; i++)
		{
			keytable[i].obj = 0;
			keytable[i].kobj = 0;
		}
		undo_obj = help_obj = 0;
		_tw(tree, ROOT);
		if(letemfly.ucol & 0xff)
		{
			vwk_handle = vwk_init();
/*			vsl_type(vwk_handle, 1);
*/			vsl_width(vwk_handle, 1);
			vswr_mode(vwk_handle, MD_REPLACE);
			rc_gtov(&wi_max, &vr);
			vs_clip(vwk_handle, 0, &vr);
			graf_mouse(M_OFF, 0l);
			for(i = 0; i < MAXALPH; i++)
			{
				if((lastdone == tree) && memcmp(&oldkbtab[i], &keytable[i], sizeof(KBDTAB)))
					_und_key(tree, oldkbtab, i, 0);
			}
			for(i = 0; i < MAXALPH; i++)
				_und_key(tree, keytable, i, 1);
			graf_mouse(M_ON, 0l);
			vwk_exit(vwk_handle);
		}
		memcpy(oldkbtab, keytable, sizeof(KBDTAB) * MAXKEYS);
	}
	lasttree = NULL;
	lastdone = tree;
	return(keys);
}

/* ----- initialize dialog ------------------------------------------------- */

MLOCAL WORD _init_fm(tree, startobj)
OBJECT	*tree;
WORD	startobj;
{
	REG WORD	obj;
	WORD	next_obj,
		edflag;
	WORD	x, y, w, h;
	OBJECT 	*tr;

	/* obj = */ next_obj = fly_obj = 0;
	obj = -1;
	edflag = TRUE;
	do
	{
		tr = &tree[++obj];
		if(((tr->ob_type & 0xff00) != GLOBOFF) && magic && (tr->ob_type & FLYER))
		{
			if(!(tr->ob_type & FLYALL))
				fly_obj = obj;
			if(!di_moveto(tree, -1, 0) && !(tr->ob_type & FLYNOCL))
			{
				tr->ob_flags |= HIDETREE;
				obj_clsize(tree, obj, &x, &y, &w, &h);
				objc_draw(tree, ROOT, MAX_DEPTH, x, y, w, h);
				tr->ob_flags &= ~HIDETREE;
			}
		}
		if(edflag && (tr->ob_flags & EDITABLE))
		{
			next_obj = obj;
			edflag = FALSE;
		}
	}
	while(!(tr->ob_flags & LASTOB));
	if(startobj > 0)
		next_obj = startobj;
	return(next_obj);
}

/* ------------------------------------------------------------------------- */
/* ----- new form_do() ----------------------------------------------------- */
/* ------------------------------------------------------------------------- */

WORD nfm_do(tree, startobj)
OBJECT	*tree;
WORD	startobj;
{
	WORD	edit_obj,
		next_obj,
		last_obj,
		kb_obj,
		obj, csr,
		eat_key,
		event,
		idx,
		mx, my, mbut,
		ks, kr, br,
		cont,
		cflag;
	WORD	i, d;

	in_do = TRUE;
	mx = 0;
	cont = TRUE;
	cflag = FALSE;
	edit_obj = 0;
	last_obj = -2;
	idx = 0;

	if((tree[ROOT].ob_type & 0xff00) == GLOBOFF)
		return(form_do(tree, startobj));
	wind_update(BEG_UPDATE);
	wind_update(BEG_MCTRL);
	do
	{
		event = evnt_multi(MU_KEYBD|MU_TIMER, 0x02, 0x01, 0x01,
					0, 0, 0, 0, 0,
					0, 0, 0, 0, 0,
					0l, 0, 0,
					&d, &d, &d, &d, &d, &d);
	}
	while(event & MU_KEYBD);
	init_keys(tree);
	next_obj = _init_fm(tree, startobj);
	graf_mkstate(&mx, &my, &mbut, &ks);
	obj = objc_find(tree, ROOT, MAX_DEPTH, mx, my);
	if((obj != -1) && (tree[obj].ob_flags & EDITABLE))
		csr = TEXT_CRSR;
	else
		csr = ARROW;
	graf_mouse(csr, 0L);
	while(cont)
	{
		if(next_obj && (edit_obj != next_obj))
		{
			edit_obj = next_obj;
			next_obj = 0;
			nob_edit(tree, edit_obj, 0, &idx, ED_INIT);
			if(cflag)
				nob_edit(tree, edit_obj, 0, &idx, ED_END);
		}
		if(cflag)
		{
			idx = _getcpos(tree, edit_obj, mx);
			nob_edit(tree, edit_obj, 0, &idx, ED_END);
			cflag = FALSE;
		}
		event = evnt_multi(MU_KEYBD|MU_BUTTON|MU_M1|MU_TIMER, 0x02, 0x01, 0x01,
					1, mx, my, 1, 1,
					0, 0, 0, 0, 0,
					0l, 200, 0,
					&mx, &my, &mbut, &ks, &kr, &br);
		if(event & MU_KEYBD)
		{
			eat_key = (!isalert && letemfly.do_key) ? letemfly.do_key(kr, ks) : FALSE;
			if(!eat_key)
			{
				kb_obj = lookup_key(kr, ks);
				if(kb_obj)
					cont = nfm_button(tree, kb_obj, 0x01, &next_obj);
				else
				{
					cont = nfm_keybd(tree, edit_obj, next_obj, kr, &next_obj, &kr);
					if(kr)
						nob_edit(tree, edit_obj, kr, &idx, ED_CHAR);
				}
			}
			else if(eat_key == 2)
			{
				cont = FALSE;
				next_obj = 0;
			}
		}
		if(event & MU_BUTTON)
		{
			next_obj = _find_kobj(tree, objc_find(tree, ROOT, MAX_DEPTH, mx, my));
			i = 0;
			while((((letemfly.conf & C_JUMP) && (next_obj == -1))
				|| ((next_obj != -1) && ((tree[next_obj].ob_flags & 0xdf) == NONE) && !fly_obj))
				&& (i++ < letemfly.flydelay) && (mbut == 0x01))
			{
				evnt_timer(10, 0);
				graf_mkstate(&mx, &my, &mbut, &ks);
				next_obj = objc_find(tree, ROOT, MAX_DEPTH, mx, my);
			}
			if(next_obj == -1)
			{
				if((last_obj >= 0) || ((last_obj < 0) && !di_moveto(tree, mx, my)))
					Bconout(2, 7);
				next_obj = 0;
			}
			else if(fly_obj && (next_obj == fly_obj) && (mbut != 0x03))
			{
				if(br == 2)
					di_center(tree);
				else
					di_fly(tree);
				last_obj = next_obj;
				next_obj = 0;
			}
			else if(((tree[next_obj].ob_flags & 0xdf) == NONE) && (!fly_obj || (mbut == 0x03)))
			{
				if(br == 2)
					di_center(tree);
				else
					di_fly(tree);
				last_obj = next_obj;
				next_obj = 0;
			}
			else
			{
				last_obj = next_obj;
				cont = nfm_button(tree, next_obj, br, &next_obj);
			}
			if((tree[next_obj & 0x7fff].ob_flags & EDITABLE) && (letemfly.conf & C_EDIT))
				cflag = TRUE;
		}
		else if(event & MU_TIMER)
			last_obj = -2;
		if(event & MU_M1)
		{
			obj = objc_find(tree, ROOT, MAX_DEPTH, mx, my);
			if((obj != -1) && (tree[obj].ob_flags & EDITABLE))
			{
				if(csr != TEXT_CRSR)
				{
					csr = TEXT_CRSR;
					graf_mouse(csr, 0L);
				}
			}
			else if(csr != ARROW)
			{
				csr = ARROW;
				graf_mouse(csr, 0L);
			}
		}
		if((!cont) || cflag || (next_obj && (next_obj != edit_obj)))
			nob_edit(tree, edit_obj, 0, &idx, ED_END);
	}
	wind_update(END_MCTRL);
	wind_update(END_UPDATE);
	graf_mouse(ARROW, 0L);
	in_do = FALSE;
	ed_flag = TRUE;
	return(next_obj);
}
