/************************************************************************
 *	TITLE:			GL_FORM.C				*
 *	PROJECT:		JAGUAR					*
 *	FUNCTION:		GEM like Library			*
 *	PROGRAMMER:		Cary Gee, Hans-Martin Krober, Andreas Binner		*
 *	DATE:			1/95					*
 *									*
 *              COPYRIGHT 1993,1994 Atari U.S. Corporation              *
 *          UNATHORIZED REPRODUCTION, ADAPTATION, DISTRIBUTION,         *
 *          PERFORMANCE OR DISPLAY OF THIS COMPUTER PROGRAM OR          *
 *        THE ASSOCIATED AUDIOVISUAL WORK IS STRICTLY PROHIBITED.       *
 *                            ALL RIGHTS RESERVED.                      *
 *									*
 ************************************************************************/

#include <stdarg.h>
#include "portab.h"
#include "glib.h"
#include "gemlib.h"
#include "joypad.h"
#include "alloc.h"
#include "slibc.h"
#include "jazobj.h"
#include "timetab.h"
#include "jazenv.h"

EXTERN WORD     _debug;

WORD            parser(VOID);
VOID            dn_list(OBJECT *, WORD);
VOID            up_list(OBJECT *, WORD);
VOID            wait_ticks(LONG num);
LONG            GetVbiCount(VOID);
VOID            draw_timeleft(VOID);
WORD            TestType(LISTINFO * info, WORD i);
WORD            DrawEntry(WORD nr, BYTE * str, WORD fh, WORD x, WORD y, WORD b, WORD flag);

/*
 * Etc
 */
VOID            _do_nothing(VOID);

/*
 * 
 * ===========================================================================
 * DEFINES
 */

#define ADJ3DPIX	3
#define ADJOUTLPIX	3
#define ADJSHADPIX	SHADOW
#define LLOWD(x) 	((UWORD)(x))
#define LLOBT(x) 	((BYTE)(x))
#define LBYTE2(x) 	(*((x)+1))
#define LBYTE3(x) 	(*(x))
#define LWGET(x) 	((WORD) *((WORD *)(x)))
#define TED_THICKNESS	22
#define LHIBT(x) 	((BYTE)(x >> 8))
#define	XADJ3DPIX	2

/*
 * 
 * ===========================================================================
 * EXTERNALS
 */

EXTERN LONG     _levels;
EXTERN LONG     _edges;
EXTERN VOID     _readpad(VOID);
extern long     mousebutt, mousex, mousey;
EXTERN LONG     vbi_count;
EXTERN LONG    *_dsp_idle;

EXTERN WORD    *v_bas_ad;
EXTERN OBJECT   jaztree[];

EXTERN VOID     voices_off(VOID);

/*
 * 
 * ===========================================================================
 * GLOBALS
 */

WORD            _ap_id;

WORD            gl_wchar;
WORD            gl_hchar;

LONG            _timeleft_average;
LONG            _timeleft_count;
LONG            _slider_ti;
WORD            _alphaslider_kidx;
LONG            _alphaslider_oldkey;
LONG            _joypad_timer;
LONG            _joypad_speed;


LONG            sl_table[] = {	/* Ramping values for slider code */
	0L,
	1L,
	10L,
	100L,
	1000L,
	10000L,
	100000L,
	1000000L,
	10000000L,
	100000000L,
	1000000000L
};

EXTERN BYTE     MenuBuffer[];

/*
 * ==========================================================================
 * FORM LIBRARY
 */

OBJECT         *akttree;

/*
 * Handle a form
 */


WORD
form_xdo(tree, first)
	REG OBJECT     *tree;
	WORD            first;
{
	LOCAL WORD      curr, what;
	LOCAL WORD      new_curr;
	LOCAL WORD      type, moved;
	LOCAL ULONG     levels, x, y;
	LOCAL LONG      i, bx;
	XOBJECT        *xobp;

	curr = first;
	new_curr = NIL;

	if (first != NIL) {
		type = tree[curr].ob_type;
		if (tree[curr].ob_flags & JOYPAD) {
			xobp = &(tree[curr].ob_spec.common->ex_ob);
		}
		objc_outline(tree, curr, NIL);
	}
	for (;;) {
		levels = 0L;
		akttree = tree;
		what = evnt_joypad(JOY | RMOUSE | LMOUSE | KEYB, &levels, &x, &y);

		if (what == JOY) {
			moved = 0;
			if (levels & FIRE_B) {
				if (type != G_XTEXTEDIT)
					moved = 1;
				new_curr = curr;
				type = tree[new_curr].ob_type;
				if (levels & JOY_UP) {
					new_curr = xobp->ob_up;
				} else if (levels & JOY_DOWN) {
					new_curr = xobp->ob_down;
				} else if (levels & JOY_LEFT) {
					new_curr = xobp->ob_left;
				} else if (levels & JOY_RIGHT) {
					new_curr = xobp->ob_right;
				}
				if (new_curr != curr)
					moved = 1;
				if ((new_curr != NIL) && (new_curr != curr)) {
					type = tree[new_curr].ob_type;
					if (tree[new_curr].ob_flags & JOYPAD) {
						xobp = &(tree[new_curr].ob_spec.common->ex_ob);
					}
					objc_outline(tree, new_curr, curr);
					curr = new_curr;
					release(JOY_UP + JOY_DOWN + JOY_LEFT + JOY_RIGHT, 0);
				}
			}
		}
		if (what == LMOUSE || what == RMOUSE) {
			moved = 0;
			new_curr = objc_find(tree, 0, (WORD) x, (WORD) y);
			type = tree[new_curr].ob_type;
			if ((tree[new_curr].ob_flags & HIDETREE) || (tree[new_curr].ob_state & DISABLED)) {
				new_curr = -1;
				type = 0;
			}
			if ((new_curr != -1) && (new_curr != curr)) {
				if (tree[new_curr].ob_flags & JOYPAD) {
					xobp = &(tree[new_curr].ob_spec.common->ex_ob);
					objc_outline(tree, new_curr, curr);
					curr = new_curr;
				} else if (tree[new_curr].ob_type == G_XBUTTON) {
					WORD            pobj;

					tree[new_curr].ob_state |= SELECTED;
					ob_draw(tree, new_curr, 1);

					pobj = get_par(tree, new_curr);
					objc_outline(tree, pobj, curr);
					curr = pobj;
					tree[new_curr].ob_state &= ~SELECTED;
					ob_draw(tree, new_curr, 1);
				}
			}
			if (new_curr != -1)
				levels = mouse_hotzone(tree, new_curr, (WORD) x, (WORD) y);
			if (!levels)
				moved = 1;
		}
		if (moved)
			continue;


		switch (type) {
		case G_XTEXTEDIT:
			frm_textedit(tree, new_curr, tree[new_curr].ob_spec.textedit, levels, what);
			break;

		case G_XENVELOPE:
			frm_envelope(tree, new_curr, tree[new_curr].ob_spec.env, levels, what);
			break;

		case G_XBUTTON:
			if (tree[new_curr].ob_flags & CHECKBOX) {
				tree[new_curr].ob_state ^= SELECTED;
				ob_draw(tree, new_curr, 0);
				release(FIRE_A, LMOUSE);
				if ((tree[new_curr].ob_spec.button->bt_callbk) != NULL)
					tree[new_curr].ob_spec.button->bt_callbk(tree, new_curr, 0, 0, 0);
			} else if ((tree[new_curr].ob_spec.button->bt_callbk) != NULL) {
				bx = (LONG) tree[new_curr].ob_spec.button->bt_callbk(tree, curr, new_curr, 0, 0);
				new_curr = curr;
				type = tree[new_curr].ob_type;
			}
			break;

		case G_XBAR:

			if (tree[curr].ob_spec.bar->ba_callbk != NULL)
				bx = (LONG) (tree[curr].ob_spec.bar->ba_callbk) (tree, curr, levels);
			else
				bx = (LONG) frm_bttnbar(tree, curr, levels, what);
			break;

		case G_XREALSLIDER:
			frm_slider(tree, new_curr, tree[new_curr].ob_spec.slider, levels, what);
			break;

		case G_XPOPUP:
			if (levels == FIRE_A)
				frm_popup(tree, new_curr, tree[new_curr].ob_spec.popup, levels, what);
			else if (levels == FIRE_C)
				if (tree[curr].ob_spec.popup->action)
					tree[curr].ob_spec.popup->action(tree, curr, tree[curr].ob_spec.popup);
			break;

		case G_XFLTSLIDER:
			frm_fltslider(tree, curr, tree[curr].ob_spec.floatslider, levels, what);
			break;

		case G_XINTSLIDER:
			frm_intslider(tree, curr, tree[curr].ob_spec.intslider, levels, what);
			break;

		case G_XTXTSLIDER:
			frm_txtslider(tree, curr, tree[curr].ob_spec.textslider, levels, what);
			break;

		case G_XALPHSLIDER:
			frm_alphslider(tree, curr, tree[curr].ob_spec.alphaslider, levels, what);
			break;

		case G_XLIST:
			frm_list(tree, curr, tree[curr].ob_spec.list, levels, what);
			break;
		}

		if (bx == -1L)
			break;
	}
	release(0xffffffff, LMOUSE);
	return 0;
}

LONG
dummy(LONG d)
{
	return d;
}

VOID
draw_timeleft(VOID)
{
	WORD            x, y, w, h, i;
	LONG            maxtime = 384L, fr, xp, time;
	LONG           *pr_table;
	EXTERN LONG    *_pr_table;
	LONG           *table;

	table = _pr_table;

	if (akttree != &jaztree[0])
		return;

	/* Time left */
	_timeleft_average += *_dsp_idle;
	_timeleft_count++;
	if (_timeleft_count == 128L) {
		objc_offset(akttree, JAZ_TLSLIDER, &x, &y);
		w = akttree[JAZ_TLSLIDER].ob_width;
		h = akttree[JAZ_TLSLIDER].ob_height;
		fr = maxtime / w;
		xp = _timeleft_average / 128L / fr;
		if (xp > (w - 1))
			xp = w - 1;
		vsf_color(RED);
		v_bar(x, y, xp, h);
		vsf_color(LWHITE);
		v_bar(x + xp, y, w - xp, h);
		_timeleft_average = _timeleft_count = 0L;
	}
	/* voices */

	objc_offset(akttree, JAZ_TLVOICES, &x, &y);
	w = akttree[JAZ_TLVOICES].ob_width;
	h = akttree[JAZ_TLVOICES].ob_height;
	fr = w / 14;
	xp = 0;
	for (i = 0; i < 14; i++) {
		time = *table;
		if (time & 8)
			xp++;
		dummy(time);
		table += 2;
	}
	xp *= fr;
	if (xp > (w - 1))
		xp = w - 1;
	vsf_color(GREEN);
	v_bar(x, y, xp, h);
	vsf_color(LWHITE);
	v_bar(x + xp, y, w - xp, h);

}

ULONG
mouse_hotzone(tree, obj, mx, my)
	OBJECT         *tree;
	WORD            obj;
	WORD            mx;
	WORD            my;
{
	WORD            nr, i;
	XOBJECT        *xobp;

	xobp = &(tree[obj].ob_spec.common->ex_ob);
	nr = xobp->num_zones;
	if (nr) {
		for (i = 0; i < nr; i++)
			if (IsInRect(mx, my, &(xobp->zones[i].rect)))
				return xobp->zones[i].joyval;
	}
	return 0;
}


WORD
IsInZone(x, y, zx, zy, zb, zh)
	WORD            x;
	WORD            y;
	WORD            zx;
	WORD            zy;
	WORD            zb;
	WORD            zh;
{
	if (x >= zx && x < zx + zb)
		if (y >= zy && y < zy + zh)
			return (1);
	return (0);
}


WORD
IsInRect(WORD x, WORD y, GRECT * r)
{
	if (x >= r->g_x && x < r->g_x + r->g_w)
		if (y >= r->g_y && y < r->g_y + r->g_h)
			return (1);
	return (0);
}

ULONG
GetMouseButton()
{
	return mousebutt;
}

VOID
release(what, mouse)
	ULONG           what;
	WORD            mouse;
{
	ULONG           l;

	do {
		_readpad();
		l = _levels & what;
	} while (l);
	if (mouse) {
		do {
		} while ((GetMouseButton() & 12) == mouse * 2);
	}
}

WORD
DrawEntry(WORD nr, BYTE * str, WORD fh, WORD x, WORD y, WORD b, WORD flag)
{
	WORD            ry;

	ry = y + nr * fh;

	if (flag) {
		vsf_color(WHITE);
		v_bar(x, ry, b, fh);
		vst_color(BLACK);
		v_gtext(x + 2, ry, str);
	} else {
		vsf_color(BLACK);
		v_bar(x, ry, b, fh);
		vst_color(WHITE);
		v_gtext(x + 2, ry, str);
	}
}

LONG
GetVbiCount()
{
	return vbi_count;
}

LONG
GetLevels()
{
	return _levels;
}

VOID
wait_ticks(LONG num)
{
	LONG            v;

	v = GetVbiCount() + num;
	do {
	} while (GetVbiCount() < v);
}


/*************************************************************/
/* Routines for handling an event in a specific object    */
/*************************************************************/

VOID
rmemcpy(BYTE * d, BYTE * s, LONG len)
{
	LONG            l;

	l = len - 1;
	do {
		d[l] = s[l];
		l--;
	} while (l >= 0);
}

BYTE            letters[] = "0123456789. ABCDEFGHIJKLMNOPQRSTUVWXYZ_";

WORD
find_letter(BYTE c)
{
	WORD            i;

	for (i = 0; i < strlen(letters); i++)
		if (letters[i] == c)
			return (i);

	return (0);
}

WORD
frm_textedit(OBJECT * tree, WORD obj, EDITINFO * info, LONG level, WORD type)
{
	WORD            curs, maxletter;
	XOBJECT        *xobp;
	WORD            x, y, b, h, cx, f, i, l;
	WORD            font_w[] = {6, 8, 8};
	WORD            font_h[] = {6, 8, 16};
	LONG            cxy[4], v;
	BYTE           *text, bu;
	BYTE           *buchst[10] = {"0ZQ_", "1 <>", "2ABC", "3DEF", "4GHI", "5JKL", "6MNO", "7PRS", "8TUV", "9WXY"};
	BYTE            sstr[40];
	WORD            keyval;
	LONG            ten[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000};

	xobp = &(tree[obj].ob_spec.common->ex_ob);
	strcpy(sstr, "%00ld");
	i = info->maxsize;
	if (i > 9)
		i = 9;
	sstr[2] = '0' + i;
	maxletter = strlen(letters) - 1;

	objc_offset(tree, obj, &x, &y);
	b = tree[obj].ob_width;
	h = tree[obj].ob_height;
	f = info->fontid;
	curs = info->cursor;
	text = info->data;
	cx = xobp->zones[0].rect.g_x + (font_w[f] * curs);
	cxy[0] = cx + 1;
	cxy[2] = cx + 1 + font_w[f];
	cxy[1] = xobp->zones[0].rect.g_y + xobp->zones[0].rect.g_h - 2;
	cxy[3] = cxy[1];

	vsl_color(RED);
	v_line(cxy);

	if (type & KEYB) {
	}
	if (level == JOY_UP || level == JOY_DOWN)
		goto up_down;

	if (level == OPTION + FIRE_A + FIRE_B + FIRE_C) {
		curs = ((mousex - 32) - xobp->zones[0].rect.g_x) / font_w[f];
		if (curs >= strlen(info->data))
			curs = strlen(info->data) - 1;
		if (curs < 0)
			curs = 0;
		info->cursor = curs;
		vsl_color(WHITE);
		v_line(cxy);
		vsl_color(RED);
		cx = xobp->zones[0].rect.g_x + (font_w[f] * curs);
		cxy[0] = cx + 1;
		cxy[2] = cx + 1 + font_w[f];
		v_line(cxy);
		wait_ticks(8);

up_down:
		if (info->usage) {
			l = info->maxsize - curs - 1;
			v = info->value;
			if ((GetMouseButton() & 8) || level == JOY_UP)
				v += ten[l];
			if ((GetMouseButton() & 4) || level == JOY_DOWN)
				v -= ten[l];

			if (v > info->max)
				v = info->max;
			if (v < info->min)
				v = info->min;
			info->value = v;
			sprintf(info->data, sstr, v);
			ob_draw(tree, obj, 0);
			vsl_color(RED);
			v_line(cxy);
		} else {
			if (GetMouseButton() & 12) {
				l = find_letter(info->data[curs]);
				if ((GetMouseButton() & 8) || level == JOY_UP)
					l++;
				if ((GetMouseButton() & 4) || level == JOY_DOWN)
					l--;

				if (l > maxletter)
					l = 0;
				if (l < 0)
					l = maxletter;

				info->data[curs] = letters[l];
				ob_draw(tree, obj, 0);
				vsl_color(RED);
				v_line(cxy);
			}
		}
		if (info->callbk)
			info->callbk(tree, obj, info);
		return;
	}
	if (level & JOY_LEFT) {
		if (curs > 0) {
			curs--;
			vsl_color(WHITE);
			v_line(cxy);
			vsl_color(RED);
			cx = xobp->zones[0].rect.g_x + (font_w[f] * curs);
			cxy[0] = cx + 1;
			cxy[2] = cx + 1 + font_w[f];
			v_line(cxy);
			release(level, 0);
			info->cursor = curs;
		}
		return;
	}
	if (level & JOY_RIGHT) {
		if (curs < strlen(info->data) && curs < info->maxsize - 1) {
			curs++;
			vsl_color(WHITE);
			v_line(cxy);
			vsl_color(RED);
			cx = xobp->zones[0].rect.g_x + (font_w[f] * curs);
			cxy[0] = cx + 1;
			cxy[2] = cx + 1 + font_w[f];
			v_line(cxy);
			release(level, 0);
			info->cursor = curs;
		}
		return;
	}
	keyval = -1L;
	if (level & KEY_0) {
		keyval = 0L;
	} else if (level & KEY_1) {
		keyval = 1L;
	} else if (level & KEY_2) {
		keyval = 2L;
	} else if (level & KEY_3) {
		keyval = 3L;
	} else if (level & KEY_4) {
		keyval = 4L;
	} else if (level & KEY_5) {
		keyval = 5L;
	} else if (level & KEY_6) {
		keyval = 6L;
	} else if (level & KEY_7) {
		keyval = 7L;
	} else if (level & KEY_8) {
		keyval = 8L;
	} else if (level & KEY_9) {
		keyval = 9L;
	}
	if (keyval >= 0) {
		if (level & FIRE_A)
			i = 1;
		else if (level & FIRE_B)
			i = 2;
		else if (level & FIRE_C)
			i = 3;
		else
			i = 0;
		if (info->usage) {
			bu = buchst[keyval][0];
			info->data[curs] = bu;
			v = atol(info->data);
			if (v > info->max)
				v = info->max;
			if (v < info->min)
				v = info->min;
			info->value = v;
			sprintf(info->data, sstr, v);
		} else {
			bu = buchst[keyval][i];
			if (bu == '<') {
				if (curs > 0) {
					memcpy(&info->data[curs - 1], &info->data[curs], strlen(&info->data[curs]) + 1);
					curs--;
				}
			} else if (bu == '>') {
				if (curs < strlen(info->data))
					memcpy(&info->data[curs], &info->data[curs + 1], info->maxsize - curs - 1);
			} else {
				if (strlen(info->data) < info->maxsize - 1) {
					rmemcpy(&info->data[curs + 1], &info->data[curs], (LONG) info->maxsize - (LONG) curs - 1L);
					info->data[info->maxsize - 1] = 0;
					info->data[curs] = bu;
					curs++;
				}
			}

			release(level & ~(FIRE_A + FIRE_B + FIRE_C), 0);
		}
		ob_draw(tree, obj, 0);
		vsl_color(RED);
		cx = xobp->zones[0].rect.g_x + (font_w[f] * curs);
		cxy[0] = cx + 1;
		cxy[2] = cx + 1 + font_w[f];
		v_line(cxy);
		if (info->callbk)
			info->callbk(tree, obj, info);
	}
	info->cursor = curs;
}


LONG
frm_envelope(OBJECT * tree, WORD obj, ENVINFO * info, LONG levels, WORD type)
{
	LONG            mx, my, ox, oy, f, t;
	WORD            x, y, b, h, d, p;
	ENVPARA        *para;

	para = info->patch_env;

	if (type == LMOUSE)
		p = (WORD) levels;	/* only mouse! */
	else
		return;

	p--;
	ox = mousex - 32;
	oy = (mousey - 72) / 2;
	objc_offset(tree, obj, &x, &y);
	b = tree[obj].ob_width;
	h = tree[obj].ob_height;
	f = 0x40000000 / h;

	tree[info->p_obj].ob_spec.textedit->value = p;
	set_limits(p, info->points);
	info->curr_point = p;
	ob_draw(tree, info->p_obj, 0);
	ob_draw(tree, obj, 0);

	if (info->locked) {
		if (!p) {
			while (mousebutt & 4) {
				mx = x;
				my = (mousey - 72) / 2;
				if (my < y)
					my = y;
				if (my > y + h - 1)
					my = y + h - 1;
				_readpad();
				parser();
				if (ox != mx || oy != my) {
					para->curr = (h - my + y) * f;
					calc_envelope(info, 0);
					ob_draw(tree, obj, 0);
					tree[info->a_obj].ob_spec.textedit->value = info->ampl[p] >> 15;
					ob_draw(tree, info->a_obj, 0);
					tree[info->t_obj].ob_spec.textedit->value = info->time[p] >> 15;
					ob_draw(tree, info->t_obj, 0);
					ox = mx;
					oy = my;
				}
			}
		}
		if (p == info->points - 1) {
			while (mousebutt & 4) {
				mx = mousex - 32;
				my = y + h;
				if (mx < x)
					mx = x;
				if (mx > x + b - 1)
					mx = x + b - 1;
				_readpad();
				parser();
				if (ox != mx || oy != my) {
					d = mx - x;
					if (d > 255)
						d = 255;
					t = timetab[d].time;
					if (t <= info->time[p - 1])
						t = info->time[p - 1] + 1;

					para->release = (-info->ampl[p - 1] / 3L / (t - info->time[p - 1])) << 15;
					calc_envelope(info, 0);
					ob_draw(tree, obj, 0);
					tree[info->a_obj].ob_spec.textedit->value = info->ampl[p] >> 15;
					ob_draw(tree, info->a_obj, 0);
					tree[info->t_obj].ob_spec.textedit->value = info->time[p] >> 15;
					ob_draw(tree, info->t_obj, 0);
				}
			}
		}
		return;
	} else {
		while (mousebutt & 4) {
			mx = mousex - 32;
			my = (mousey - 72) / 2;
			if (!p)
				mx = x;
			if (p == info->points - 1)
				my = y + h;
			if (mx < x)
				mx = x;
			if (mx > x + b - 1)
				mx = x + b - 1;
			if (my < y)
				my = y;
			if (my > y + h - 1)
				my = y + h - 1;

			_readpad();
			parser();
			if (ox != mx || oy != my) {
				d = mx - x;
				if (d > 255)
					d = 255;
				info->time[p] = timetab[d].time;
				if (p)
					if (info->time[p] < info->time[p - 1])
						info->time[p] = info->time[p - 1];
				if (p < info->points - 1)
					if (info->time[p] > info->time[p + 1])
						info->time[p] = info->time[p + 1];

				info->ampl[p] = (h - my + y) * f;
				ob_draw(tree, obj, 0);
				tree[info->a_obj].ob_spec.textedit->value = info->ampl[p] >> 15;
				ob_draw(tree, info->a_obj, 0);
				tree[info->t_obj].ob_spec.textedit->value = info->time[p] >> 15;
				ob_draw(tree, info->t_obj, 0);
				calc_envelope(info, 1);
				ox = mx;
				oy = my;
			}
		}
	}

	tree[info->a_obj].ob_spec.textedit->value = info->ampl[p] >> 15;
	ob_draw(tree, info->a_obj, 0);
	tree[info->t_obj].ob_spec.textedit->value = info->time[p] >> 15;
	ob_draw(tree, info->t_obj, 0);

	/* HACK! But works */
	if (!p || p == info->points - 1)
		tree[9].ob_state |= DISABLED;
	else
		tree[9].ob_state &= ~DISABLED;
	ob_draw(tree, 9, 0);
	ob_draw(tree, obj, 0);
}

LONG
frm_slider(OBJECT * tree, WORD obj, SLIDEINFO * info, LONG val, WORD type)
{
	LONG            my, oldmy, dy, off, in = 1L, mx;
	WORD            x, y, b, h, c;
	XOBJECT        *xobp;
	LONG            ten[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000};

	xobp = &(tree[obj].ob_spec.common->ex_ob);

	objc_offset(tree, obj, &x, &y);
	b = tree[obj].ob_width;
	h = tree[obj].ob_height;

	oldmy = (mousey - 72) / 2;
	off = oldmy - xobp->zones[0].rect.g_y;
	mx = mousex - 32;

	if (val == OPTION) {
		c = (xobp->zones[1].rect.g_x + xobp->zones[1].rect.g_w - mx) / 6;
		if (GetMouseButton() & 4) {
			info->curr -= ten[c];
			if (info->curr < info->min)
				info->curr = info->min;
			info->flags |= 1;
			ob_draw(tree, obj, 0);
			if (info->callbk)
				info->callbk(tree, obj, info);

		}
		if (GetMouseButton() & 8) {
			info->curr += ten[c];
			if (info->curr > info->max)
				info->curr = info->max;
			info->flags |= 1;
			ob_draw(tree, obj, 0);
			if (info->callbk)
				info->callbk(tree, obj, info);

		}
		return;
	}
	if (mousebutt & 4) {
		wait_ticks(10L);
		if (!(mousebutt & 4)) {
			info->curr += in;
			if (info->curr > info->max)
				info->curr = info->max;
			info->flags |= 1;
			ob_draw(tree, obj, 0);
			if (info->callbk)
				info->callbk(tree, obj, info);
		}
	}
	do {
		parser();
		_readpad();
		my = (mousey - 72) / 2;
		if (my != oldmy || (_levels & JOY_UP) || (_levels & JOY_DOWN)) {
			if (my != oldmy) {
				dy = my - y - 12 - off;
				if (dy < 0)
					dy = 0;
				if (dy > h - 32)
					dy = h - 32;

				dy = h - 32 - dy;
				info->curr = info->min + (info->max - info->min) * dy / (LONG) (h - 32);
			}
			if (_levels & JOY_UP) {
				info->curr += in;
				if (info->curr > info->max)
					info->curr = info->max;

				wait_ticks(_slider_ti);
				_slider_ti--;
				if (_slider_ti < 0)
					_slider_ti = 0;
			} else if (_levels & JOY_DOWN) {
				info->curr -= in;
				if (info->curr < info->min)
					info->curr = info->min;

				wait_ticks(_slider_ti);
				_slider_ti--;
				if (_slider_ti < 0)
					_slider_ti = 0;
			} else
				_slider_ti = 10L;

			info->flags |= 1;
			ob_draw(tree, obj, 0);
			oldmy = my;
			if (info->callbk)
				info->callbk(tree, obj, info);
		}
	} while ((mousebutt & 4) || (_levels & JOY_UP) || (_levels & JOY_DOWN));

	_slider_ti = 10L;
}


LONG
frm_popup(OBJECT * tree, WORD obj, POPUPINFO * info, LONG val, WORD type)
{
	WORD            x, y, b, h, anz, f, boxx, boxh, boxb, off, iy, boxy, fh;
	WORD            what, i, row, oldrow;
	ULONG           mx, my, levels, oldmx, oldmy;
	BYTE            temp1[255], temp2[255];
	WORD            font_h[] = {6, 8, 16};
	XOBJECT        *xobp;

	if (info->type & LOCKPOP) {
		form_alert(FRM_ALERT, info->lock_str);
		return;
	}
	xobp = &(tree[obj].ob_spec.common->ex_ob);
	vst_color(BLACK);
	objc_offset(tree, obj, &x, &y);
	b = tree[obj].ob_width;
	h = tree[obj].ob_height;

	f = info->fontid;
	fh = font_h[f];
	anz = info->entries;
	boxh = anz * fh;
	off = info->index * fh;
	if (off > y)
		off = y;

	boxx = xobp->zones[0].rect.g_x;
	boxy = xobp->zones[0].rect.g_y - off;
	boxb = xobp->zones[0].rect.g_w;

	if (boxy + boxh > 200)
		boxy = 200 - boxh;
	SaveBackground(boxx - 1, boxy - 1, boxb + 4, boxh + 4);
	v_bar_shadow(boxx, boxy, boxb, boxh);
	vst_font(f);
	iy = boxy;
	iy++;
	vst_wrmode(1);

	/*
	 * Need two string buffers, because v_gtext doesn't wait until the
	 * GPU has drawn the text !!!!
	 */

	for (i = 0; i < anz; i++) {
		if (i & 1) {
			GetPopItem(info->data, i, temp1);
			v_gtext(boxx + 2, iy, temp1);
		} else {
			GetPopItem(info->data, i, temp2);
			v_gtext(boxx + 2, iy, temp2);
		}
		iy += fh;
	}

	release(FIRE_A | FIRE_C, LMOUSE);

	oldrow = -1;
	row = info->index;
	oldmx = mousex - 32;
	oldmy = (mousey - 72) / 2;
	vst_wrmode(1);
	for (;;) {
		mx = mousex - 32;
		my = (mousey - 72) / 2;
		_readpad();
		/* parser();	 */

		if (mx != oldmx || my != oldmy) {
			if (IsInZone((WORD) mx, (WORD) my, boxx, boxy, boxb, boxh))
				row = (my - boxy) / fh;
			else
				row = -1;
			oldmx = mx;
			oldmy = my;
		}
		if (_levels & JOY_UP)
			if (row > 0) {
				row--;
				_wait_up();
			}
		if (_levels & JOY_DOWN)
			if (row < anz - 1) {
				row++;
				_wait_up();
			}
		if (_levels & FIRE_C)
			row = -1;

		if (row != oldrow) {
			if (oldrow >= 0) {
				GetPopItem(info->data, oldrow, temp1);
				DrawEntry(oldrow, temp1, fh, boxx, boxy, boxb, 1);
			}
			if (row >= 0) {
				GetPopItem(info->data, row, temp2);
				DrawEntry(row, temp2, fh, boxx, boxy, boxb, 0);
			}
			oldrow = row;
		}
		if ((mousebutt & 4) || (_levels & FIRE_A) || (_levels & FIRE_C)) {
			if (row >= 0)
				info->index = row;
			break;
		}
	}

	LoadBackground(boxx - 1, boxy - 1, boxb + 4, boxh + 4);
	ob_draw(tree, obj, 0);
	release(FIRE_A | FIRE_C, LMOUSE);
	vst_color(BLACK);

	if (info->callbk)
		info->callbk(tree, obj, info);

}

LONG
frm_bttnbar(OBJECT * tree, WORD curr, LONG k, WORD what)
{
	BARINFO        *bap;
	WORD            n = -1;
	LONG            x;
	WORD            found;
	REG WORD        i;

	bap = tree[curr].ob_spec.bar;

	if (k & KEY_0)
		n = 0;
	else if (k & KEY_1)
		n = 1;
	else if (k & KEY_2)
		n = 2;
	else if (k & KEY_3)
		n = 3;
	else if (k & KEY_4)
		n = 4;
	else if (k & KEY_5)
		n = 5;
	else if (k & KEY_6)
		n = 6;
	else if (k & KEY_7)
		n = 7;
	else if (k & KEY_8)
		n = 8;
	else if (k & KEY_9)
		n = 9;

	if ((n < bap->ba_first) || (n >= (bap->ba_first + bap->ba_num))) {
		return 0L;
	}
	found = NIL;
	for (i = tree[curr].ob_head; 1; i = tree[i].ob_next) {
		if (tree[i].ob_type == G_XBUTTON) {
			if ((tree[i].ob_spec.button->bt_smchar - '0') == n) {
				found = i;
				break;
			}
		}
		if (i == tree[curr].ob_tail)
			break;
	}
	if (found == NIL)
		return NIL;
	if (tree[found].ob_state & DISABLED)
		return 0L;
	if (tree[found].ob_flags & RBUTTON) {
		if ((bap->ba_rcurr != NIL) &&
		    (found != bap->ba_rcurr) &&
		    (tree[bap->ba_rcurr].ob_flags & RBUTTON)) {
			Deselect(bap->ba_rcurr);
			ob_draw(tree, bap->ba_rcurr, MAXDEPTH);
		}
		bap->ba_rcurr = found;
	}
	Select(found);
	ob_draw(tree, found, MAXDEPTH);

	if ((tree[found].ob_spec.button->bt_callbk) != NULL) {
		i = -10;
		x = (LONG) tree[found].ob_spec.button->bt_callbk(tree, curr,
							       found, n, k);
	}
	if (tree[found].ob_flags & ABUTTON) {
		if (i != -10)
			for (i = 0; i < 5000; i++);
		Deselect(found);
		ob_draw(tree, found, MAXDEPTH);

	}
	bap->ba_curr = found;

	return x;
}

/*
 * Handle floating pt slider
 */

VOID
frm_fltslider(OBJECT * tree, WORD obj, SLFLTINFO * slinfo, LONG edges, WORD type)
{
	WORD            slwidth, slheight;
	BYTE            intstr[10];
	BYTE            fracstr[10];
	WORD            boxx, boxy;
	WORD            intx, fracx, texty;
	WORD            tblindex, index, maxindex;
	WORD            modify_int;
	LONG            temp;
	BYTE            ch;
	LONG            keyval;
	WORD            flag = FALSE;
	LONG            pts[4];
	LONG(*fctp) (OBJECT *, WORD, SLFLTINFO *);

	vst_bgcolor(LWHITE);
	vst_color(BLACK);

	vst_font(slinfo->fontid);
	vst_wrmode(0);

	slwidth = (slinfo->intspaces + slinfo->fracspaces + 5) *
		slinfo->charwidth;
	slheight = 2 * slinfo->charheight;
	boxx = slinfo->sliderx;
	boxy = slinfo->slidery + ((slinfo->charheight == 6) ?
			       slinfo->charheight + 3 : slinfo->charheight);
	intx = boxx + 2 * slinfo->charwidth;
	fracx = intx + (slinfo->intspaces * slinfo->charwidth) +
		slinfo->charwidth;
	texty = boxy + (slinfo->charheight / 2);

	maxindex = slinfo->intspaces + slinfo->fracspaces - 1;
	index = slinfo->cursor_index;
	if (index < slinfo->intspaces) {
		modify_int = 1;
		tblindex = slinfo->intspaces - index;
	} else {
		index -= slinfo->intspaces;
		modify_int = 0;
		tblindex = slinfo->fracspaces - index;
	}

	if (edges & JOY_UP) {
		if (modify_int) {
			if ((slinfo->intval + sl_table[tblindex]) < slinfo->maxint)
				slinfo->intval += sl_table[tblindex];
			else
				slinfo->intval = slinfo->maxint;
		} else {
			if ((slinfo->fracval + sl_table[tblindex]) < slinfo->maxfrac)
				slinfo->fracval += sl_table[tblindex];
			else
				slinfo->fracval = slinfo->maxfrac;
		}
		flag = TRUE;
	}
	if (edges & JOY_DOWN) {
		if (modify_int) {
			if (slinfo->intval - sl_table[tblindex] >= 0L)
				slinfo->intval -= sl_table[tblindex];
			else
				slinfo->intval = 0L;
		} else {
			if (slinfo->fracval - sl_table[tblindex] >= 0L)
				slinfo->fracval -= sl_table[tblindex];
			else
				slinfo->fracval = 0L;
		}
		flag = TRUE;
	}
	keyval = -1L;
	if (edges & KEY_0) {
		keyval = 0L;
	} else if (edges & KEY_1) {
		keyval = 1L;
	} else if (edges & KEY_2) {
		keyval = 2L;
	} else if (edges & KEY_3) {
		keyval = 3L;
	} else if (edges & KEY_4) {
		keyval = 4L;
	} else if (edges & KEY_5) {
		keyval = 5L;
	} else if (edges & KEY_6) {
		keyval = 6L;
	} else if (edges & KEY_7) {
		keyval = 7L;
	} else if (edges & KEY_8) {
		keyval = 8L;
	} else if (edges & KEY_9) {
		keyval = 9L;
	}
	ltoa(slinfo->intval, slinfo->intspaces, intstr);
	ltoa(slinfo->fracval, slinfo->fracspaces, fracstr);

	if (keyval >= 0L) {
		if (modify_int) {
			ch = intstr[index];
			temp = (long) (ch - '0');
			temp *= sl_table[tblindex];
			slinfo->intval -= temp;
			temp = keyval * sl_table[tblindex];
			if ((slinfo->intval + temp) < slinfo->maxint)
				slinfo->intval += temp;
		} else {
			ch = fracstr[index];
			temp = (LONG) (ch - '0');
			temp *= sl_table[tblindex];
			slinfo->fracval -= temp;
			temp = keyval * sl_table[tblindex];
			if ((slinfo->fracval + temp) < slinfo->maxfrac)
				slinfo->fracval += temp;
		}
		flag = TRUE;
	}
	if ((edges & FIRE_A) || (edges & JOY_RIGHT) || (edges & JOY_LEFT) || (keyval >= 0)) {
		/* Erase old line prepare to move cursor */
		vsl_color(LWHITE);
		index = slinfo->cursor_index;
		if (index >= slinfo->intspaces)
			index++;/* Space for the . */
		pts[0] = intx + (slinfo->charwidth * index);
		pts[1] = texty + slinfo->charheight + 2;
		pts[2] = pts[0] + slinfo->charwidth;
		pts[3] = pts[1];
		v_line(pts);
		if ((edges & JOY_RIGHT) || (keyval >= 0)) {
			if (slinfo->cursor_index < maxindex)
				slinfo->cursor_index++;
			else
				slinfo->cursor_index = 0L;
		}
		if (edges & JOY_LEFT) {
			if (slinfo->cursor_index > 0)
				slinfo->cursor_index--;
			else
				slinfo->cursor_index = maxindex;
		}
		if (edges & FIRE_A) {
			slinfo->cursor_index = maxindex;
		}
	}
	/*
	 * Print the slider values
	 */
	ltoa(slinfo->intval, slinfo->intspaces, intstr);
	ltoa(slinfo->fracval, slinfo->fracspaces, fracstr);

	v_gtext(intx, texty, intstr);
	v_gtext(fracx - slinfo->charwidth, texty, ":");
	v_gtext(fracx, texty, fracstr);

	/*
	 * Redraw the cursor
	 */
	vsl_color(GREEN);
	index = slinfo->cursor_index;
	if (index >= slinfo->intspaces)
		index++;	/* Space for the . */
	pts[0] = intx + (slinfo->charwidth * index);
	pts[1] = texty + slinfo->charheight + 2;
	pts[2] = pts[0] + slinfo->charwidth;
	pts[3] = pts[1];
	v_line(pts);

	fctp = (LONG(*) (OBJECT *, WORD, SLFLTINFO *)) slinfo->callbk;
	if (fctp != NULL)
		(*fctp) (tree, obj, slinfo);
}


/*
 * Handle alpha slider
 */

VOID
frm_alphslider(OBJECT * tree, WORD obj, SLALPHINFO * slinfo, LONG edges, WORD type)
{
	WORD            slwidth, slheight;
	WORD            boxx, boxy;
	WORD            intx, fracx, texty;
	WORD            tblindex, index, maxindex;
	WORD            modify_main;
	LONG            keyval = -1L;
	REG WORD        i;
	WORD            flag = FALSE;
	LONG            pts[4];
	LONG(*fctp) (OBJECT *, WORD, SLALPHINFO *);
	BYTE           *buchst[10] = {"0ZQ_", "1   ", "2ABC", "3DEF", "4GHI", "5JKL", "6MNO", "7PRS", "8TUV", "9WXY"};

	vst_bgcolor(LWHITE);
	vst_color(BLACK);

	vst_font(slinfo->fontid);
	vst_wrmode(0);

	slwidth = (slinfo->mainspaces + slinfo->extspaces + 5) *
		slinfo->charwidth;
	slheight = 2 * slinfo->charheight;
	boxx = slinfo->sliderx;
	boxy = slinfo->slidery + ((slinfo->charheight == 6) ?
			       slinfo->charheight + 3 : slinfo->charheight);
	intx = boxx + 2 * slinfo->charwidth;
	fracx = intx + (slinfo->mainspaces * slinfo->charwidth) +
		slinfo->charwidth;
	texty = boxy + (slinfo->charheight / 2);

	maxindex = slinfo->mainspaces + slinfo->extspaces - 1;
	index = slinfo->cursor_index;
	if (index < slinfo->mainspaces) {
		modify_main = 1;
		tblindex = slinfo->mainspaces - index;
	} else {
		index -= slinfo->mainspaces;
		modify_main = 0;
		tblindex = slinfo->extspaces - index;
	}

	keyval = -1L;
	if (edges & KEY_0) {
		keyval = 0L;
	} else if (edges & KEY_1) {
		keyval = 1L;
	} else if (edges & KEY_2) {
		keyval = 2L;
	} else if (edges & KEY_3) {
		keyval = 3L;
	} else if (edges & KEY_4) {
		keyval = 4L;
	} else if (edges & KEY_5) {
		keyval = 5L;
	} else if (edges & KEY_6) {
		keyval = 6L;
	} else if (edges & KEY_7) {
		keyval = 7L;
	} else if (edges & KEY_8) {
		keyval = 8L;
	} else if (edges & KEY_9) {
		keyval = 9L;
	}
	if (keyval >= 0) {
		if (keyval != _alphaslider_oldkey)
			_alphaslider_kidx = 0;
		else {
			_alphaslider_kidx++;
			if (_alphaslider_kidx == 4)
				_alphaslider_kidx = 0;
		}
		if (modify_main) {
			slinfo->mainstr[index] =
				(WORD) buchst[keyval][_alphaslider_kidx];
			alpha_char2ix(slinfo->mainstr, slinfo->mainix,
				      slinfo->table);
		} else {
			slinfo->extstr[index] =
				(WORD) buchst[keyval][_alphaslider_kidx];
			alpha_char2ix(slinfo->extstr, slinfo->extix,
				      slinfo->table);
		}
		_alphaslider_oldkey = keyval;
	}
	if (edges & JOY_UP) {
		if (modify_main) {
			slinfo->mainix[index] += 1;
			if (slinfo->mainix[index] >= slinfo->tabitems)
				slinfo->mainix[index] = 0;
		} else {
			slinfo->extix[index] += 1;
			if (slinfo->extix[index] >= slinfo->tabitems)
				slinfo->extix[index] = 0;
		}
		flag = TRUE;
	}
	if (edges & JOY_DOWN) {
		if (modify_main) {
			slinfo->mainix[index] -= 1;
			if (slinfo->mainix[index] <= 0)
				slinfo->mainix[index] = slinfo->tabitems - 1;
		} else {
			slinfo->extix[index] -= 1;
			if (slinfo->extix[index] <= 0)
				slinfo->extix[index] = slinfo->tabitems - 1;
		}
		flag = TRUE;
	}
	if (edges & KEY_HASH) {
		if (modify_main) {
			for (i = index; i < 8; i++) {
				slinfo->mainstr[i] = ' ';
			}
			alpha_char2ix(slinfo->mainstr, slinfo->mainix,
				      slinfo->table);
		} else {
			for (i = index; i < 3; i++) {
				slinfo->extstr[i] = ' ';
			}
			alpha_char2ix(slinfo->extstr, slinfo->extix,
				      slinfo->table);
		}
		flag = FALSE;
		keyval = '#';
	}
	if (flag) {
		if (modify_main)
			slinfo->mainstr[index] =
				slinfo->table[slinfo->mainix[index]];
		else
			slinfo->extstr[index] =
				slinfo->table[slinfo->extix[index]];
	}
	if ((edges & FIRE_A) || (edges & JOY_RIGHT) || (edges & JOY_LEFT) || (keyval >= 0)) {
		/* Erase old line prepare to move cursor */
		vsl_color(LWHITE);
		index = slinfo->cursor_index;
		if (index >= slinfo->mainspaces)
			index++;/* Space for the . */
		pts[0] = intx + (slinfo->charwidth * index);
		pts[1] = texty + slinfo->charheight + 2;
		pts[2] = pts[0] + slinfo->charwidth;
		pts[3] = pts[1];
		v_line(pts);
		if ((edges & JOY_RIGHT) || (keyval == '#')) {
			if ((keyval == '#') && modify_main) {
				slinfo->cursor_index = 8L;
			} else if (slinfo->cursor_index < maxindex)
				slinfo->cursor_index++;
			else
				slinfo->cursor_index = 0L;
		}
		if (edges & JOY_LEFT) {
			if (slinfo->cursor_index > 0)
				slinfo->cursor_index--;
			else
				slinfo->cursor_index = maxindex;
		}
		if (edges & FIRE_A) {
			slinfo->cursor_index = maxindex;
		}
	}
	/*
	 * Print the slider values
	 */

	v_gtext(intx, texty, slinfo->mainstr);
	if (slinfo->extflag) {
		v_gtext(fracx - slinfo->charwidth, texty, ".");
		v_gtext(fracx, texty, slinfo->extstr);
	}
	/*
	 * Redraw the cursor
	 */
	vsl_color(GREEN);
	index = slinfo->cursor_index;
	if (index >= slinfo->mainspaces)
		index++;	/* Space for the . */
	pts[0] = intx + (slinfo->charwidth * index);
	pts[1] = texty + slinfo->charheight + 2;
	pts[2] = pts[0] + slinfo->charwidth;
	pts[3] = pts[1];
	v_line(pts);

	fctp = (LONG(*) (OBJECT *, WORD, SLALPHINFO *)) slinfo->callbk;
	if (fctp != NULL)
		(*fctp) (tree, obj, slinfo);
}


/*
 * Handle integer slider
 */

VOID
frm_intslider(OBJECT * tree, WORD obj, SLINTINFO * slinfo, LONG edges, WORD type)
{
	WORD            slwidth, slheight;
	BYTE            intstr[15];
	WORD            boxx, boxy;
	WORD            intx, texty;
	WORD            tblindex, index, maxindex;
	LONG            temp;
	LONG            intval;
	BYTE            ch;
	LONG            keyval;
	LONG            pts[4];
	WORD            flag = FALSE;
	LONG(*fctp) (OBJECT *, WORD, SLINTINFO *);

	vst_bgcolor(LWHITE);
	vst_color(BLACK);
	vst_font(slinfo->fontid);
	vst_wrmode(0);

	slwidth = (slinfo->intspaces + 5) * slinfo->charwidth;
	/* slwidth = slinfo->slwidth; */
	slheight = 2 * 8 /* slinfo->charheight */ ;
	boxx = slinfo->sliderx;
	boxy = slinfo->slidery + ((slinfo->charheight == 6) ?
			       slinfo->charheight + 3 : slinfo->charheight);

	intx = (boxx + (slwidth / 2)) - ((slinfo->intspaces *
					  slinfo->charwidth) / 2);
	/* intx = boxx + 2 * slinfo->charwidth; */

	texty = boxy + (slinfo->charheight / 2);

	maxindex = slinfo->intspaces - 1;
	index = slinfo->cursor_index;

	tblindex = slinfo->intspaces - index;

	if (edges & JOY_UP) {
		if (slinfo->signextend) {
			if (slinfo->intval + sl_table[tblindex] < slinfo->maxint)
				slinfo->intval += sl_table[tblindex];
			else
				slinfo->intval = slinfo->maxint;
		} else {
			if ((ULONG) slinfo->intval + (ULONG) sl_table[tblindex] <
			    (ULONG) slinfo->maxint)
				slinfo->intval += sl_table[tblindex];
			else
				slinfo->intval = slinfo->maxint;
		}
		flag = TRUE;
	}
	if (edges & JOY_DOWN) {
		if (slinfo->signextend) {
			intval = slinfo->intval;
			if ((slinfo->intval - sl_table[tblindex]) >=
			    (-slinfo->maxint))
				slinfo->intval -= sl_table[tblindex];
			else
				slinfo->intval = -slinfo->maxint;
		} else {
			if (slinfo->intval - sl_table[tblindex] >= 0)
				slinfo->intval -= sl_table[tblindex];
			else
				slinfo->intval = 0L;
		}
		flag = TRUE;
	}
	keyval = -1L;
	if (edges & KEY_0) {
		keyval = 0L;
	} else if (edges & KEY_1) {
		keyval = 1L;
	} else if (edges & KEY_2) {
		keyval = 2L;
	} else if (edges & KEY_3) {
		keyval = 3L;
	} else if (edges & KEY_4) {
		keyval = 4L;
	} else if (edges & KEY_5) {
		keyval = 5L;
	} else if (edges & KEY_6) {
		keyval = 6L;
	} else if (edges & KEY_7) {
		keyval = 7L;
	} else if (edges & KEY_8) {
		keyval = 8L;
	} else if (edges & KEY_9) {
		keyval = 9L;
	}
	if (slinfo->signextend)
		itoa(slinfo->intval, slinfo->intspaces, intstr);
	else
		ltoa(slinfo->intval, slinfo->intspaces, intstr);
	if (keyval >= 0L) {
		flag = TRUE;

		ch = intstr[index];
		temp = (long) (ch - '0');
		temp *= sl_table[tblindex];
		if (slinfo->signextend && ((short) slinfo->intval < 0))
			slinfo->intval += temp;
		else
			slinfo->intval -= temp;
		temp = keyval * sl_table[tblindex];
		if (slinfo->signextend && ((WORD) slinfo->intval < 0)) {
			if (slinfo->intval - temp > (-slinfo->maxint))
				slinfo->intval -= temp;
		} else if ((ULONG) slinfo->intval + (ULONG) sl_table[tblindex] <
			   (ULONG) slinfo->maxint)
			slinfo->intval += temp;
	}
	if ((edges & FIRE_A) || (edges & JOY_RIGHT) || (edges & JOY_LEFT) || (keyval >= 0)) {
		flag = TRUE;
		/* Erase old line prepare to move cursor */
		vsl_color(LWHITE);
		index = slinfo->cursor_index;

		pts[0] = intx + (slinfo->charwidth * index);
		pts[1] = texty + slinfo->charheight + 2;
		pts[2] = pts[0] + slinfo->charwidth;
		pts[3] = pts[1];
		v_line(pts);
		if ((edges & JOY_RIGHT) || (keyval >= 0)) {
			if (slinfo->cursor_index < maxindex)
				slinfo->cursor_index++;
			else
				slinfo->cursor_index = 0 + slinfo->signextend;
		}
		if (edges & JOY_LEFT) {
			if (slinfo->cursor_index > (0 + slinfo->signextend))
				slinfo->cursor_index--;
			else
				slinfo->cursor_index = maxindex;
		}
		if (edges & FIRE_A) {
			slinfo->cursor_index = maxindex;
		}
	}
	if (edges & KEY_STAR) {
		slinfo->intval = (long) (-(short) slinfo->intval);
	}
	/*** Print the slider values ****/

	if (slinfo->signextend)
		itoa((short) slinfo->intval, (int) slinfo->intspaces, intstr);
	else
		ltoa(slinfo->intval, (int) slinfo->intspaces, intstr);

	v_gtext(intx, texty, intstr);

	/*** Redraw the cursor ***/
	vsl_color(GREEN);

	index = slinfo->cursor_index;

	pts[0] = intx + (slinfo->charwidth * index);
	pts[1] = texty + slinfo->charheight + 2;
	pts[2] = pts[0] + slinfo->charwidth;
	pts[3] = pts[1];
	v_line(pts);


	fctp = (LONG(*) (OBJECT *, WORD, SLINTINFO *)) slinfo->callbk;
	if (fctp != NULL)
		(*fctp) (tree, obj, slinfo);
}

/*
 * Handle Text Slider
 */

VOID
frm_txtslider(OBJECT * tree, WORD obj, SLTXTINFO * slinfo, LONG edges, WORD type)
{
	WORD            slwidth, slheight;
	WORD            boxx, boxy;
	WORD            xpos, ypos;
	WORD            flag = FALSE;
	LONG(*fctp) (OBJECT *, WORD, SLTXTINFO *);

	vst_bgcolor(LWHITE);
	vst_color(BLACK);
	vst_font(slinfo->fontid);
	vst_wrmode(0);

	slwidth = (slinfo->maxlength + 5) * slinfo->charwidth;
	slheight = 2 * slinfo->charheight;
	boxx = slinfo->sliderx;
	boxy = slinfo->slidery + ((slinfo->charheight == 6) ?
			       slinfo->charheight + 3 : slinfo->charheight);
	/* xpos = boxx + 2 * slinfo->charwidth; */
	xpos = (boxx + (slwidth / 2)) -
		((slinfo->maxlength * slinfo->charwidth) / 2);
	ypos = boxy + (slinfo->charheight / 2);

	if (edges & JOY_UP) {
		if ((slinfo->index + 1) < slinfo->maxitems)
			slinfo->index++;
		else
			slinfo->index = 0L;
		flag = TRUE;
	}
	if (edges & JOY_DOWN) {
		if ((slinfo->index - 1) >= 0L)
			slinfo->index--;
		else
			slinfo->index = slinfo->maxitems - 1;
		flag = TRUE;
	}
	v_gtext(xpos, ypos, (BYTE *) (slinfo->table[slinfo->index]));

	if (flag) {
		fctp = (LONG(*) (OBJECT *, WORD, SLTXTINFO *)) slinfo->callbk;
		if (fctp != NULL)
			(*fctp) (tree, obj, slinfo);
	}
}


WORD
TestType(LISTINFO * info, WORD i)
{
	BYTE            types[255], *poi, *s, *atype;

	atype = ((BYTE **) info->data)[i * info->ncols + info->type_col];
	strcpy(types, info->match);
	s = types;
	do {
		poi = s;
		s = strchr(s, '|');
		if (s)
			*s = 0;
		if (!strcmp(poi, atype))
			return (1);
		if (s)
			s++;
	} while (s);
	return (0);
}

VOID
frm_list(OBJECT * tree, WORD obj, LISTINFO * info, LONG edges, WORD type)
{
	WORD            i, y, sx, sy, p, t;

	if ((edges & JOY_UP) && ((info->index - 1) >= 0)) {
		if (info->type_col >= 0) {
			i = info->index;
			i--;
			while (i >= 0 && !TestType(info, i))
				i--;
			if (i >= 0) {
				while (info->index != i) {
					info->index--;
					up_list(tree, obj);
				}
				if (info->scrolled)
					(*info->scrolled) (tree, obj, info);
			}
		} else {
			info->index--;

			up_list(tree, obj);
			if (info->scrolled)
				(*info->scrolled) (tree, obj, info);
		}
	} else if ((edges & JOY_DOWN) &&
		   ((info->index + 1) < info->maxitems)) {
		if (info->type_col >= 0) {
			i = info->index;
			i++;
			while (i < info->maxitems && !TestType(info, i))
				i++;
			if (i < info->maxitems) {
				while (info->index != i) {
					info->index++;
					dn_list(tree, obj);
				}
				if (info->scrolled)
					(*info->scrolled) (tree, obj, info);
			}
		} else {
			info->index++;
			dn_list(tree, obj);
			if (info->scrolled)
				(*info->scrolled) (tree, obj, info);
		}
	} else if (edges & FIRE_A) {
		if (edges & OPTION) {
			objc_offset(tree, obj, &sx, &sy);
			y = (mousey - 72) / 2 - sy - 23;
			p = y / (info->charheight + 2);
			p = p + info->index - info->visitems / 2;
			if (p >= 0 && p < info->maxitems) {
				if (info->type_col >= 0)
					t = TestType(info, p);
				else
					t = 1;

				if (t) {
					if (info->index > p) {
						do {
							info->index--;
							up_list(tree, obj);
						} while (info->index > p);
					} else if (info->index < p) {
						do {
							info->index++;
							dn_list(tree, obj);
						} while (info->index < p);
					}
				}
			}
			if (info->scrolled)
				(*info->scrolled) (tree, obj, info);
		}
		if (info->callbk)
			(*info->callbk) (tree, obj, info);
		objc_outline(tree, obj, NIL);
	}
}

VOID
dn_list(tree, obj)
	OBJECT         *tree;
	WORD            obj;

{
	REG LISTINFO   *list = tree[obj].ob_spec.list;
	REG BYTE      **data = (BYTE **) list->data;
	REG WORD        i, j, l;
	WORD            n;
	WORD            sx, sy, x, y, w;
	WORD            ix;
	WORD            area[8];
	MFDB            src;
	MFDB            dst;
	WORD            dy;
	WORD            cy = list->cy;


	vst_font(list->fontid);
	vst_color(BLACK);

	/* ----- red2raw current line in black */
	objc_offset(tree, obj, &sx, &sy);

	y = list->cly;
	x = sx;
	dy = list->charheight + 2;
	n = list->ncols;
	ix = list->ncols * (list->index - 1);
	for (j = 0; j < n; j++) {
		x = sx + list->cx[j];
		v_gtext(x + 2, y, data[ix++]);
	}


	/* ----- do the blit */

	src.fd_addr = (LONG) v_bas_ad;
	src.fd_width = 0x4400L;
	src.fd_height = 200L;

	dst.fd_addr = (LONG) MenuBuffer;
	dst.fd_width = 0x4400L;
	dst.fd_height = 100L;

	area[0] = sx + 2;
	area[1] = sy + cy + dy;
	area[2] = tree[obj].ob_width - 14;
	area[3] = tree[obj].ob_height - cy - dy;

	area[4] = 0;
	area[5] = 0;
	area[6] = area[2];
	area[7] = area[3];

	vro_cpyfm(12, area, &src, &dst);

	area[0] = 0;
	area[1] = 0;
	area[2] = tree[obj].ob_width - 14;
	area[3] = tree[obj].ob_height - cy - dy;

	area[4] = sx + 2;
	area[5] = sy + cy;
	area[6] = area[2];
	area[7] = area[3];

	vro_cpyfm(12, area, &dst, &src);

	/* ----- redraw NEW current line in green */
	vst_color(BLUE);
	y = list->cly;
	x = sx;
	n = list->ncols;
	ix = list->ncols * list->index;
	for (j = 0; j < n; j++) {
		x = sx + list->cx[j];
		v_gtext(x + 2, y, data[ix++]);
	}
	vst_color(BLACK);

	/* ----- clear & redraw the last line */


	l = list->index + list->visitems / 2;
	l = (l > list->maxitems) ? list->maxitems : l;
	y = sy + list->cy + list->visitems * dy;
	x = sx - 2 + list->cx[0];
	vsf_color(LWHITE);
	for (i = 0; i < (n - 1); i++) {
		x = sx + list->cx[i];
		w = list->cx[i + 1] - list->cx[i] - 2;
		v_bar(x, y, w, dy);
	}
	x = sx + list->cx[i];
	if (n > 1)
		w = tree[obj].ob_width - list->cx[i] - 14;
	else
		w = tree[obj].ob_width - 14;
	v_bar(x, y, w, dy);

	if ((list->index + list->visitems / 2) < list->maxitems) {
		ix = l * n;
		for (j = 0; j < n; j++) {
			x = sx + list->cx[j];
			v_gtext(x + 2, y, data[ix++]);
		}
	}
}

VOID
up_list(tree, obj)
	OBJECT         *tree;
	WORD            obj;

{
	REG LISTINFO   *list = tree[obj].ob_spec.list;
	REG BYTE      **data = (BYTE **) list->data;
	REG WORD        i, j, l;
	WORD            n;
	WORD            sx, sy, x, y, w;
	WORD            ix;
	WORD            area[8];
	MFDB            src;
	MFDB            dst;
	WORD            dy;
	WORD            cy = list->cy;

	vst_font(list->fontid);
	vst_color(BLACK);

	/* ----- redraw current line in black */
	objc_offset(tree, obj, &sx, &sy);

	y = list->cly;
	x = sx;
	dy = list->charheight + 2;
	n = list->ncols;
	ix = list->ncols * (list->index + 1);
	for (j = 0; j < n; j++) {
		x = sx + list->cx[j];
		v_gtext(x + 2, y, data[ix++]);
	}


	/* ----- do the blit */

	src.fd_addr = (LONG) v_bas_ad;
	src.fd_width = 0x4400L;
	src.fd_height = 200L;

	dst.fd_addr = (LONG) MenuBuffer;
	dst.fd_width = 0x4400L;
	dst.fd_height = 100L;

	if (list->fontid == FNT_SMALL)
		j = 4;
	else
		j = 6;

	area[0] = sx + 2;
	area[1] = sy + cy;
	area[2] = tree[obj].ob_width - 14;
	area[3] = tree[obj].ob_height - cy - dy - j;

	area[4] = 0;
	area[5] = 0;
	area[6] = area[2];
	area[7] = area[3];

	vro_cpyfm(12, area, &src, &dst);

	area[0] = 0;
	area[1] = 0;
	area[2] = tree[obj].ob_width - 14;
	area[3] = tree[obj].ob_height - cy - dy - j;

	area[4] = sx + 2;
	area[5] = sy + cy + dy;
	area[6] = area[2];
	area[7] = area[3];

	vro_cpyfm(12, area, &dst, &src);

	/* ----- redraw NEW current line in green */
	vst_color(BLUE);
	y = list->cly;
	x = sx;
	n = list->ncols;
	ix = list->ncols * list->index;
	for (j = 0; j < n; j++) {
		x = sx + list->cx[j];
		v_gtext(x + 2, y, data[ix++]);
	}
	vst_color(BLACK);

	/* ----- clear & redraw the first line */

	l = list->index - list->visitems / 2;
	l = (l < 0) ? 0 : l;
	y = sy + list->cy;	/* + list->visitems * dy; */
	x = sx - 2 + list->cx[0];
	vsf_color(LWHITE);
	for (i = 0; i < (n - 1); i++) {
		x = sx + list->cx[i];
		w = list->cx[i + 1] - list->cx[i] - 2;
		v_bar(x, y, w, dy);
	}
	x = sx + list->cx[i];
	if (n > 1)
		w = tree[obj].ob_width - list->cx[i] - 14;
	else
		w = tree[obj].ob_width - 14;

	v_bar(x, y, w, dy);


	if ((list->index - list->visitems / 2) >= 0) {
		ix = l * n;
		for (j = 0; j < n; j++) {
			x = sx + list->cx[j];
			v_gtext(x + 2, y, data[ix++]);
		}
	}
}

/*
 * ==========================================================================
 * EVENT LIBRARY
 */



ULONG
evnt_joypad(mask, joypad, x, y)
	WORD            mask;
	ULONG          *joypad;
	ULONG          *x;
	ULONG          *y;
{
	WORD            r;

	do {
		parser();
	}
	while (GetVbiCount() < _joypad_timer);
	_joypad_speed--;
	if (_joypad_speed < 0)
		_joypad_speed = 0L;

	for (;;) {
		draw_timeleft();
		r = parser();
		_readpad();
		/* Maximum mouse range */
		mousex = MIN(mousex, 414);
		mousex = MAX(mousex, 32);
		mousey = MIN(mousey, 464);
		mousey = MAX(mousey, 72);
		/* Calculate obj coordinates */
		*x = mousex - 32;
		*y = (mousey - 72) / 2;
		*joypad = _levels;
		if (_levels == PAUSE)
			voices_off();

		if (_levels == KEY_STAR)
			jaz_debug();

		if (_levels == KEY_HASH)
			_debug = 1;
		if (_levels == KEY_0)
			_debug = 0;

		/* if ((mask & JOY) && (_levels & ~FIRE_B)) */
		if ((mask & JOY) && _levels) {
			_joypad_timer = GetVbiCount() + _joypad_speed;
			return (JOY);
		}
		if ((mask & LMOUSE) && (mousebutt & 4)) {
			_joypad_timer = GetVbiCount() + _joypad_speed;
			return (LMOUSE);
		}
		if ((mask & RMOUSE) && (mousebutt & 8)) {
			_joypad_timer = GetVbiCount() + _joypad_speed;
			return (RMOUSE);
		}
		_joypad_speed = 10L;
	}
	return 0;
}


VOID
_do_nothing(VOID)
{

}
