
#include "proto.h"

static int		ob_next(OBJECT *,int);
static int	 	ob_find(OBJECT *,int,int);
static int		do_popup(OBJECT *,boolean,int,int *);

#ifdef DEBUG
extern void _check_hotkeys(OBJECT *);
#endif

static int ob_next(OBJECT *tree,int start)
{
	reg OBJECT *obj;
	reg int i = start;

	if (start<tree->ob_head)
		start = tree->ob_head;

	do
	{
		obj = tree + i;
		if ((obj->ob_state & DISABLED)==0 && (obj->ob_flags & (SELECTABLE|HIDETREE))==SELECTABLE)
			return(i);
		i = obj->ob_next;
	}
	while (i>=0);

	return(FAIL);
}

static int ob_find(OBJECT *tree,int x,int y)
{
	reg int pos = objc_find(tree,ROOT,1,x,y);

	if (pos>0 && ob_next(tree,pos)==pos)
		return(pos);
	else
		return(FAIL);
}

static int do_popup(OBJECT *menu,boolean wrap,int last,int *dc)
{
	reg int current,old = FAIL,ret = FAIL;
	XEVENT events;

	_mouse_pos(&events.ev_mmox,&events.ev_mmoy);
	if ((current = ob_find(menu,events.ev_mmox,events.ev_mmoy))<=0)
		current = last;

	events.ev_mflags = MU_BUTTON|MU_M1|MU_KEYBD;
	events.ev_mbstate = events.ev_mbclicks = events.ev_bmask = 1;

	for (;;)
	{
		if (current!=old)
		{
			if (old>0)
				objc_change(menu,old,0,desk.g_x,desk.g_y,desk.g_w,desk.g_h,menu[old].ob_state & (~SELECTED),1);

			if (current>0)
				objc_change(menu,current,0,desk.g_x,desk.g_y,desk.g_w,desk.g_h,menu[current].ob_state | SELECTED,1);

			old = current;
		}

		if (objc_find(menu,ROOT,1,events.ev_mmox,events.ev_mmoy)<0)
		{
			events.ev_mm1flags = 0;
			*(GRECT *) &events.ev_mm1x = *(GRECT *) &menu->ob_x;
		}
		else
		{
			events.ev_mm1flags = events.ev_mm1width = events.ev_mm1height = 1;
			events.ev_mm1x = events.ev_mmox;
			events.ev_mm1y = events.ev_mmoy;
		}

		Event_Multi(&events);
		if (events.ev_mwich & MU_BUTTON)
		{
			if (!(events.ev_mmobutton & 1))
			{
				ret = ob_find(menu,events.ev_mmox,events.ev_mmoy);
				break;
			}
			else
				events.ev_mbstate = 0;
		}

		if (events.ev_mwich & MU_M1)
		{
			current = ob_find(menu,events.ev_mmox,events.ev_mmoy);
			events.ev_mm2x = events.ev_mmox;
			events.ev_mm2y = events.ev_mmoy;
		}

		if (events.ev_mwich & MU_KEYBD)
		{
			int exitmode = FALSE;

			switch (events.ev_mkreturn & 0x00ff)
			{
			case 13:
			case 32:
				ret = current;
				exitmode = TRUE;
				break;
			case 27:
				ret = FAIL;
				exitmode = TRUE;
			}

			if (exitmode==FALSE)
			{
				switch (events.ev_mkreturn>>8)
				{
				case 97:
					ret = FAIL;
					exitmode = TRUE;
					break;
				case 71:
					if (events.ev_mmokstate & 3)
					{
						reg int j;

						for (j=menu->ob_head;j<=menu->ob_tail;j++)
							if ((menu[j].ob_state & DISABLED)==0 && (menu[j].ob_flags & (SELECTABLE|HIDETREE))==SELECTABLE)
								 current = j;
					}
					else
						current = ob_next(menu,menu->ob_head);
					break;
				case 75:
				case 72:
					{
						reg int j,temp = FAIL;

						if (current>menu->ob_head)
						{
							for (j=menu->ob_head;j<current;j++)
								if ((menu[j].ob_state & DISABLED)==0 && (menu[j].ob_flags & (SELECTABLE|HIDETREE))==SELECTABLE)
									 temp = j;
						}

						if (temp<0 && (wrap || current<0))
						{
							for (j=menu->ob_head;j<=menu->ob_tail;j++)
								if ((menu[j].ob_state & DISABLED)==0 && (menu[j].ob_flags & (SELECTABLE|HIDETREE))==SELECTABLE)
									 temp = j;
						}

						if (temp>0)
							current = temp;
					}
					break;
				case 77:
				case 80:
					if (current<menu->ob_tail)
					{
						if (current<0)
							current = ob_next(menu,menu->ob_head);
						else
						{
							int temp = ob_next(menu,menu[current].ob_next);
							if (temp>0 && temp!=current)
								current = temp;
							else
								current = ob_next(menu,menu->ob_head);
						}
					}
					else if (wrap)
						current = ob_next(menu,menu->ob_head);
					break;
				default:
					{
						int obj;

						if ((obj=_is_hotkey(menu,scan_2_ascii(events.ev_mkreturn,events.ev_mmokstate)))>0)
						{
							if ((menu[obj].ob_state & DISABLED)==0 && (menu[obj].ob_flags & (SELECTABLE|HIDETREE))==SELECTABLE)
							{
								if (old>0)
									objc_change(menu,old,0,desk.g_x,desk.g_y,desk.g_w,desk.g_h,menu[old].ob_state & (~SELECTED),1);
								objc_change(menu,obj,0,desk.g_x,desk.g_y,desk.g_w,desk.g_h,menu[obj].ob_state | SELECTED,1);
								Event_Timer(100,0);

								exitmode = TRUE;
								ret = obj;
							}
						}
					}
				}
			}

			if (exitmode)
				break;
		}
	}

	if (ret>0)
		*dc = (events.ev_mbreturn>=2 || (events.ev_mmokstate & 3));
	return (ret);
}

int Popup(POPUP *popup,int mode,int center,int x,int y,int *index,int select)
{
	reg DIAINFO *info = popup->p_info;
	reg OBJECT *menu = popup->p_menu,*tree=(info!=NULL) ? info->di_tree : NULL;
	reg int current = FAIL,exit = FAIL,object;
	int dc = FALSE;

	graf_mouse(ARROW,NULL);

	if (tree!=NULL)
	{
		object = (mode & POPUP_CYCLE) ? popup->p_cycle : popup->p_parent;
		if (object>0)
			ob_draw_chg(info,object,NULL,tree[object].ob_state|SELECTED,FALSE);
	}

	{
		reg int i;

		for (i=menu->ob_head;i<=menu->ob_tail;i++)
		{
			if (menu[i].ob_flags & SELECTABLE)
				menu[i].ob_state &= ~SELECTED;

			if (!(menu[i].ob_flags & HIDETREE))
			{
				if (i==select)
				{
					if (mode & POPUP_CHECK)
						menu[i].ob_state |= CHECKED;
					if (current>=0)
						menu[current].ob_state &= ~CHECKED;
					current=i;
				}
				else if (menu[i].ob_state & CHECKED)
				{
					if (!(mode & POPUP_CHECK) || current>=0)
						menu[i].ob_state &= ~CHECKED;
					else
						current = i;
				}
			}
		}
	}

	if (!(mode & POPUP_CYCLE))
	{
		int sx,sy,sw = menu->ob_width>>1,sh = menu->ob_height>>1,dummy;
		long mem = 0l;
		MFDB dial;

		_mouse_pos(&sx,&sy);
		if (tree!=NULL && popup->p_button>0)
		{
			int bx,by;
			objc_offset(tree,popup->p_button,&bx,&by);
			if (x<=0)
				x = bx;
			if (y<=0)
				y = by;
		}

		switch (center)
		{
		case CENTER:
			{
				INFOVSCR *vscr;

				form_center(menu,&dummy,&dummy,&dummy,&dummy);
				if (get_cookie(COOKIE_VSCR,(long *) &vscr) && (vscr->cookie==COOKIE_XBRA))
				{
					menu->ob_x = vscr->x + (vscr->w>>1) - sw;
					menu->ob_y = vscr->y + (vscr->h>>1) - sh;
				}
			}
			break;
		case XYPOS:
			menu->ob_x = x;
			menu->ob_y = y;
			break;
		case MOUSEPOS:
			menu->ob_x = sx - sw;
			menu->ob_y = sy - sh;
			break;
		case XPOS:
			menu->ob_x = x;
			menu->ob_y = sy - sh;
			break;
		case YPOS:
			menu->ob_x = sx - sw;
			menu->ob_y = y;
			break;
		case OBJPOS:
			menu->ob_x = x;
			menu->ob_y = y;
			if (current>=0)
			{
				menu->ob_x -= menu[current].ob_x;
				menu->ob_y -= menu[current].ob_y;
			}
		}

		Min(&menu->ob_x,clip.v_x2 - menu->ob_width - 3);
		Max(&menu->ob_x,clip.v_x1 + 3);
		Min(&menu->ob_y,clip.v_y2 - menu->ob_height - 3);
		Max(&menu->ob_y,clip.v_y1 + 3);

		sx = menu->ob_x - 3;
		sy = menu->ob_y - 3;
		sw = menu->ob_width + 6;
		sh = menu->ob_height + 6;

		_beg_ctrl();
		mfdb(&dial,NULL,sw,sh,0,planes);

		{
			reg long len = mfdb_size(&dial);

			if ((long) Malloc(-1l)>(len+4096l))
			{
				if ((mem = (long) Malloc(len+256l))>0l)
				{
					dial.fd_addr	= (int *) ((mem & 0xfffffffel) + 2);
					_bitblt(menu,&dial,TRUE);
					goto draw_popup;
				}
			}
		}

		form_dial(FMD_START,sx,sy,sw,sh,sx,sy,sw,sh);

		draw_popup:
		objc_draw(menu,ROOT,MAX_DEPTH,sx,sy,sw,sh);

	#ifdef DEBUG
		check_hotkeys(menu);
	#endif

		_popup++;
		exit = do_popup(menu,popup->p_wrap,current,&dc);
		_popup--;

		if (mem)
		{
			_bitblt(menu,&dial,FALSE);
			Mfree((void *) mem);
		}
		else
			form_dial(FMD_FINISH,sx,sy,sw,sh,sx,sy,sw,sh);
		_end_ctrl();
	}
	else
	{
		if (current<0 || current>=menu->ob_tail)
			exit = ob_next(menu,menu->ob_head);
		else
		{
			int temp = ob_next(menu,menu[current].ob_next);
			exit = (temp>0) ? temp : ob_next(menu,menu->ob_head);
		}
	}

	if (tree!=NULL && object>0)
		ob_draw_chg(info,object,NULL,tree[object].ob_state & (~SELECTED),FALSE);

	if (exit>0)
	{
		if (mode & POPUP_CHECK)
		{
			if (current>=0)
				menu[current].ob_state &= ~CHECKED;
			menu[exit].ob_state |= CHECKED;
		}

		if (tree!=NULL && popup->p_set && popup->p_button>0)
		{
			ob_set_text(tree,popup->p_button,ob_get_text(menu,exit,0));
			ob_draw_chg(info,popup->p_button,NULL,FAIL,FALSE);
			if (popup->p_cycle>0)
				ob_draw_chg(info,popup->p_cycle,NULL,FAIL,FALSE);
		}

		if (index!=NULL)
		{
			reg int i = 1,radio = 0;

			do
			{
				if ((menu[i].ob_flags & (SELECTABLE|HIDETREE))==SELECTABLE)
				{
					if (i==exit)
					{
						*index = radio;
						break;
					}
					radio++;
				}
				i = menu[i].ob_next;
			} while (i>=0);
		}
	}

	if (exit<=0)
		return(0);
	else
		return ((dc) ? (exit|0x8000) : exit);
}
