

/* MenuMgr_event.c */

#include <stdio.h>
#include <sys/types.h>
#include <ctype.h>
#include <X10/Xlib.h>
#include <X10/Xkeyboard.h>

#include "MenuMgr.h"

extern int		mm_debug;
extern FontFamily	mm_Fonts;
extern XAssocTable *	mm_AssocTable;
extern Cursor		mm_Cursor;
extern int		mm_Freeze;	/* from MenuMgr_textmenu.c */

static int		mm_MenusRunning = 0;	/* menus are running */
static int		mm_NameWaiting = 0;	/* waiting for activate */
static int		mm_Choosing = 0;	/* selecting items */
MenuPtr			mm_CurrentMenu = NULL;	/* externally visible */

#define max(a,b)	((a)>(b) ? (a) : (b))
#define DEBUG( n,s )	if(mm_debug && (n)>=mm_debug) printf s
#define DEBUG_LEVEL(n)	(mm_debug && (n)>=mm_debug)
 
/* ---------------------------------------------------------------------- */

int
mm_HandleEvent(ep)

	XEvent *	ep;
{
Window		ew;
int		magic;
caddr_t		addr;
MenuPtr		mp = NULL;
MenuBarPtr	mbp = NULL;
ItemPtr		ip = NULL;

	ew = ep->window;
	
	addr = XLookUpAssoc(mm_AssocTable,ew);

	if( addr == NULL )		/* not from one of our windows */
	{
		/* event not for us */
		magic = -1;
	}
	else
		magic = *((int *)addr);

	if( magic == MBAR_MAGIC )
	{
		mbp = (MenuBarPtr) addr;
		mm_HandleMBarEvent(mbp,ep);
	}
	else if( magic == MENU_MAGIC )
	{
		mp = (MenuPtr) addr;
		mm_HandleMenuEvent(mp,ep);
	}
	else if( magic == ITEM_MAGIC )
	{
		ip = (ItemPtr) addr;
		mm_HandleItemEvent(ip,ep);
	}

	if( DEBUG_LEVEL(1) )
		printev(stderr,ep->type);

	return 0;
}

/* ---------------------------------------------------------------------- */

static int
printev(file,type)

	FILE *		file;
	unsigned int 	type;
{
	switch(type)
	{
		case	KeyPressed:
			fprintf(file,"KeyPressed\n");
			break;
		case	ButtonPressed:
			fprintf(file,"ButtonPressed\n");
			break;
		case	ButtonReleased:
			fprintf(file,"ButtonReleased\n");
			break;
		case	EnterWindow:
			fprintf(file,"EnterWindow\n");
			break;
		case	LeaveWindow:
			fprintf(file,"LeaveWindow\n");
			break;
		case	ExposeWindow:
			fprintf(file,"ExposeWindow\n");
			break;
		case	ExposeRegion:
			fprintf(file,"ExposeRegion\n");
			break;
		case	UnmapWindow:
			fprintf(file,"UnmapWindow\n");
			break;
		default:
			fprintf(file,"XERROR: not one of our events!!!\n");
			break;
	}
	return 0;
}

/* ---------------------------------------------------------------------- */

static int
mm_KillMenus(mbp)

	MenuBarPtr	mbp;
{
	if( mm_MenusRunning )
	{
		mm_MenusRunning = 0;
		if( mm_CurrentMenu )
			(*mm_CurrentMenu->menuProc)		
				(msgDraw,mm_CurrentMenu,1,0);
		mm_HiliteMenuPtr(mbp,NULL);
		mm_CurrentMenu = NULL;
		mm_Choosing = 0;
		if( mm_Freeze )
			XUngrabServer();
		XUngrabMouse();
		XFlush();
	}
	return 0;
}

/* ---------------------------------------------------------------------- */

static int
mm_DoChoice( mp, item )

	MenuPtr		mp;
	int		item;
{
int	i;
DefsPtr	d = &mm_Defs;

	/* flash, erase, call, then unhilite */
	
	if( ITEM_IS_ENABLED( mp, item ) )
	{
		for(i=0; i<(2*d->flashtimes+1);i++ )
		{
			(*mp->menuProc)
				(msgChoose,mp,0,item);
			XSync(0);
			mpause(d->flashmsecs);
		}
	}
	(*mp->menuProc)
		( msgDraw,mp,1,0);
	if( ITEM_IS_ENABLED( mp, item ) && mp->menuBar 
	   && mp->menuBar->mbarMenuCallback)
		(*mp->menuBar->mbarMenuCallback)(mp,item);
	mm_HiliteMenuPtr( mp->menuBar, NULL );
	return 0;
}

/* ---------------------------------------------------------------------- */

static int
mm_GrabMouse( w, mask, funcname )

	Window		w;
	int		mask;
	char *		funcname;
{
int	i;
int	retrys = 5;	/* try for 5 seconds */
int	x,y,subw;
short	state;

	for( i=retrys; i>0; i-- )
	{
		if( XGrabMouse( w, mm_Cursor, mask ) )
		{
			XQueryMouseButtons(w,&x,&y,&subw,&state);
			if( state )	/* any buttons still depressed? */
				return 1;
			break;			
		}
	}
	XUngrabMouse();
	XFlush();
	if( i==0 )
		printf("%s: unable to grab mouse!\n",funcname);
	return 0;
}

/* ---------------------------------------------------------------------- */

static int
mm_HandleMBarEvent(mbp,ep)

	MenuBarPtr	mbp;
	XEvent *	ep;
{
static char *funcname = "mm_HandleMBarEvent";
Window		ew, esw;
XKeyPressedEvent *kev;
char *		keystr;
int		n;
int		item;
WindowInfo	pwi, nwi, mwi;
int		thisWidth, thisHeight;
DefsPtr		d = &mm_Defs;
	
	if( mbp==NULL )
		return -1;

	ew = ep->window;
	esw = ep->subwindow;

	if( ew == mbp->mbarParentWindow )
	{
		switch( ep->type )
		{

		case ButtonReleased:

			if( mm_Choosing )
			{
				item = mm_CurrentMenu->menuLastItem;
				if( item > 0 )
					mm_DoChoice(mm_CurrentMenu,item);
			}
			mm_KillMenus(mbp);
			break;

		case KeyPressed:

			if( esw || mm_MenusRunning || mm_Choosing 
			    || mm_NameWaiting )
				break;
			kev = (XKeyPressedEvent *)ep;
			if( kev->detail & MetaMask )
			{
				kev->detail &= ~MetaMask;
				keystr = XLookupMapping(kev,&n);
				if( n==1 )
					mm_MenuKey(mbp,(int)(*keystr));
			}
			break;
			
		default:
			if( DEBUG_LEVEL(1) )
			{
				fprintf(stderr,"%s: unrecognized ",funcname);
				fprintf(stderr,"parent window event: ");
				printev(stderr,ep->type);
			}
			break;
				
		}	/* end of switch */
	}		/* end of ParentWindow */

	else if( ew == mbp->mbarNameWindow )
	{
		switch( ep->type )
		{

		case ButtonPressed:

			if( mm_MenusRunning )
				mm_KillMenus(mbp);

			if( mm_NameWaiting )
			{
				mm_NameWaiting = 0;
				mm_HiliteNameBar( mbp, 0 );
				XUngrabMouse();
				XFlush();
			}
			else
			{
				mm_NameWaiting = 1;
				if( !mm_GrabMouse(mbp->mbarNameWindow,
					ButtonPressed|ButtonReleased
					|LeaveWindow,funcname) )
						mm_NameWaiting = 0;
				else
					mm_HiliteNameBar( mbp, 1 );
			}
			break;

		case LeaveWindow:
			
			mm_NameWaiting = 0;
			mm_HiliteNameBar( mbp, 0 );
			XUngrabMouse();
			XFlush();
			break;

		case ButtonReleased:

			if( mm_MenusRunning )
				mm_KillMenus(mbp);

			if( mm_NameWaiting )
			{
				if( mbp->mbarNameCallback )
					(*mbp->mbarNameCallback)(mbp);
				mm_NameWaiting = 0;
				mm_HiliteNameBar( mbp, 0 );
				XUngrabMouse();
				XFlush();
			}
			break;

		case ExposeRegion:
			if( esw )
				break;
			mm_DrawNameBar( mbp );
			break;

		case ExposeWindow:

			if( esw )
				break;
			/* resize if necessary */
			XQueryWindow( mbp->mbarParentWindow, &pwi );
			XQueryWindow( ew, &nwi );
			thisWidth = pwi.width-(2*d->extNborder);
			thisHeight = (mm_Fonts.fi[mm_Fonts.tallest]->height)+
				(2*d->intNborder);

			/* the changewindow call will generate another
				exposewindow event */
			if( thisWidth != nwi.width )
				XChangeWindow( ew, thisWidth, thisHeight );
			else
			{
				mm_DrawNameBar( mbp );
				mm_SetNewMenuBarPos( mbp );
			}
			break;

		default:
			if( DEBUG_LEVEL(1) )
			{
				fprintf(stderr,"%s: unrecognized ",funcname);
				fprintf(stderr,"name bar event: ");
				printev(stderr,ep->type);
			}			
			break;

		}	/* end switch */
	}		/* end NameWindow */

	else if( ew == mbp->mbarMenuWindow )
	{

		switch( ep->type )
		{

		case ButtonPressed:

			if( mm_MenusRunning )
				mm_KillMenus(mbp);
			mm_MenusRunning = 1;

			if( esw )
			{
				mm_CurrentMenu = 
				   (MenuPtr)XLookUpAssoc(mm_AssocTable,esw);
				if( mm_CurrentMenu->menuMagic != MENU_MAGIC )
					mm_CurrentMenu = NULL;

				if( mm_CurrentMenu )
				{
					mm_HiliteMenuPtr(mbp,mm_CurrentMenu);
					if( mm_Freeze )
						XGrabServer();
					(*mm_CurrentMenu->menuProc)
						(msgDraw,mm_CurrentMenu,0,0);
				}
	
			}	/* end if(esw) */

			if( !mm_GrabMouse(mbp->mbarParentWindow,
				ButtonReleased,funcname) )
					mm_KillMenus(mbp);			
			break;

		case ExposeRegion:
			mm_DrawMenuBar( mbp );
			break;

		case ExposeWindow:
			if( mm_MenusRunning )
				mm_KillMenus(mbp);

			/* resize if necessary */
			XQueryWindow( mbp->mbarParentWindow, &pwi );
			XQueryWindow( ew, &mwi );
			thisWidth = pwi.width+1;
			thisHeight = (mm_Fonts.fi[mm_Fonts.tallest]->height)+
				(2*d->intMborder);

			/* the changewindow call will generate another
				exposewindow event */
			if( thisWidth != mwi.width )
				XChangeWindow( ew, thisWidth, thisHeight );
			else
			{
				mm_SetNewMenuBarPos( mbp );
				mm_DrawMenuBar( mbp );
			}
			break;

		case ButtonReleased:

			mm_KillMenus(mbp);
			break;	

		default:
			if( DEBUG_LEVEL(1) )
			{
				fprintf(stderr,"%s: unrecognized ",funcname);
				fprintf(stderr,"menu bar event: ");
				printev(stderr,ep->type);
			}
			break;

		}	/* end switch */
	}		/* end if() */

	return 0;
}

/* ---------------------------------------------------------------------- */

/* an event occurred in the menu title window */

static int
mm_HandleMenuEvent( mp, ep )	

	MenuPtr		mp;
	XEvent *	ep;
{
static char *funcname = "mm_HandleMenuEvent";
Window		ew;
XLeaveWindowEvent *lwe;
int		twidth;		/* total width of this menu title window */
DefsPtr		d = &mm_Defs;

	ew = ep->window;

	if( ew != mp->menuTitleWindow )
		return(-1);

	switch(ep->type)
	{

	case EnterWindow:

		if( !mm_MenusRunning )
			break;

		if( mm_Choosing )
		{
			mm_Choosing = 0;
			if( mm_CurrentMenu && mm_CurrentMenu != mp )
			{
				(*mp->menuProc)
					(msgDraw,mm_CurrentMenu,1,0);
			}
		}

		mm_CurrentMenu = mp;
		mm_HiliteMenuPtr( mp->menuBar, mp );
		(*mp->menuProc)
			( msgDraw,mp,0,0 );	
		break;

	case LeaveWindow:

		if( !mm_MenusRunning )
			break;

		/* check if going down into item windows */

		lwe = (XLeaveWindowEvent *) ep;
		twidth = max(mp->menuTitleOffWidth,
			mp->menuTitleOnWidth) +
			2*mm_Fonts.fi[mm_Fonts.widest]->width +
			2*d->intMborder;

		if( lwe->x>0 && lwe->y>0 && lwe->x<twidth )
		{
			mm_Choosing = 1;
		}
		else
		{
			(*mp->menuProc)
				(msgDraw,mp,1,0);
			mm_CurrentMenu = NULL;
			mm_HiliteMenuPtr( mp->menuBar, NULL );
		}
		break;

	default:
		if( DEBUG_LEVEL(1) )
		{
			fprintf(stderr,"%s: unrecognized ",funcname);
			fprintf(stderr,"menu event: ");
			printev(stderr,ep->type);
		}
		break;

	}	/* end switch */

	return 0;
}

/* ---------------------------------------------------------------------- */

static int
mm_HandleItemEvent( ip, ep )	

	ItemPtr		ip;
	XEvent *	ep;
{
static char *funcname = "mm_HandleItemEvent";
XLeaveWindowEvent *lwe;
Window		ew;
int		relx, rely;
Window		subw;
int		item;

	ew = ep->window;
	if( ew != ip->itemWindow )
		return -1;

	DEBUG(1,("ITEM event in <%s>: \n",ip->itemLabel ));

	if( !mm_Choosing )
		return 0;
	item = ip->itemNumber;

	switch( ep->type )
	{

	case ButtonReleased:
		mm_DoChoice(mm_CurrentMenu,item);
		mm_KillMenus(mm_CurrentMenu->menuBar);
		break;

	case EnterWindow:
		(*mm_CurrentMenu->menuProc)
			(msgChoose,mm_CurrentMenu,0,item);
		break;

	case LeaveWindow:
		lwe = (XLeaveWindowEvent *) ep;
		XInterpretLocator( ip->itemMenu->menuWindow, 
			&relx, &rely, &subw, lwe->location );
		if( !subw )	/* just left menu; turn off last item */
			(*mm_CurrentMenu->menuProc)
				(msgChoose,ip->itemMenu,0,0);
		break;

	default:
		if( DEBUG_LEVEL(1) )
		{
			fprintf(stderr,"%s: unrecognized ",funcname);
			fprintf(stderr,"item event: ");
			printev(stderr,ep->type);
		}
		break;

	}	/* end switch */

	return 0;
}

/* ---------------------------------------------------------------------- */

int
mm_MenuKey( mbp, ch )

	MenuBarPtr	mbp;
	int		ch;
{
MenuPtr		mp;
ItemPtr		ip;
TextDataPtr	tdp;
FUNCTION	f;
char		c;
int *		intp;
DefsPtr		d = &mm_Defs;

	if( !mbp || mm_MenusRunning || mm_Choosing || mm_NameWaiting )
		return -1;

	c = (char)ch;
	if( islower(c) )
		c = toupper(c);

	for( 	mp=mbp->mbarMList;
		mp;
		mp=mp->menuNext )
	{

		for( 	ip=mp->menuItems;
			ip;
			ip = ip->itemNext )
		{

			/* if it's not a text item, skip it */
			intp = (int *) ip->itemData;
			if( *intp != TEXT_MAGIC )
				continue;

			tdp = (TextDataPtr) ip->itemData;
			if( ITEM_IS_ENABLED(mp,ip->itemNumber) && 
				(tdp->tdEquiv==c) )
			{
				mm_HiliteMenuPtr( mp->menuBar, mp );
				XSync(0);
				/* make sure it's seen */
				mpause(d->flashmsecs);	

				f = mp->menuBar->mbarMenuCallback;
				if( f )
					(*f)(mp,ip->itemNumber);
				mm_HiliteMenuPtr( mp->menuBar, NULL );
				return 0;
			}
		}
	}
	return 0;
}
