/* $Revision Header *** Header built automatically - do not edit! ***********
 *
 *	(C) Copyright 1990 by MXM
 *
 *	Name .....: MXMSub.c
 *	Created ..: Saturday 06-Jan-90 23:30
 *	Revision .: 5
 *
 *	Date            Author          Comment
 *	=========       ========        ====================
 *	09-Mar-90	Olsen		Rework for Aztec 5.0
 *	14-Jan-90	Olsen		Renamed to mxm.library
 *	07-Jan-90       Olsen           Added InvertKeyMap
 *	07-Jan-90       Olsen           - Empty log message -
 *	06-Jan-90       Olsen           Created this file!
 *
 * $Revision Header ********************************************************/
 #define REVISION 4

VOID
CalcDimensions(cd_String,cd_Width,cd_Height,cd_MaxWidth)
UBYTE *cd_String;
LONG *cd_Width,*cd_Height,cd_MaxWidth;
{
	LONG i,cd_InLine = 0,cd_NumLines = 0;

	*cd_Width = *cd_Height = 0;

	for(i = 0 ; i < strlen(cd_String) ; i++)
	{
		if(cd_String[i] == '\n' || cd_InLine == cd_MaxWidth)
		{
			if(cd_InLine > *cd_Width)
				*cd_Width = cd_InLine;

			cd_NumLines++;
			cd_InLine = 0;

			continue;
		}

		if(cd_String[i] == '\33')
		{
			while(cd_String[i] != 'm' && cd_String[i] != 'w' && i < strlen(cd_String))
				i++;

			if(i >= strlen(cd_String))
				i = strlen(cd_String) - 1;

			continue;
		}

		if(cd_String[i] < ' ')
			continue;

		cd_InLine++;
	}

	*cd_Height = cd_NumLines;

	if(cd_InLine > *cd_Width)
		*cd_Width = cd_InLine;
}

SHORT pop_computestate();
VOID pop_do_highlighting();
VOID pop_render();
VOID pop_draw_menuitem();
struct MenuItem *pop_getitem();
SHORT pop_strlen();

#define SETX 2
#define SETY 1

/*
 * pop_computestate()
 *
 * This function checks to see where the mouse pointer is in relation to
 * the various menu items in the menu.  If it is inside one of them, it
 * returns which one (indexed by its linear position in the MenuItem list
 * with 0 being the first one).  If not, returns -1.
 *
 * Possible future enhancement: keep a set of state variables containing
 * the UL and LR corners of the last-known select box; this would make
 * a quick check possible and would cut down the computation for short
 * mouse movements (the most common).
 */

SHORT
pop_computestate(win, menu)
struct Window *win;
struct Menu *menu;
{
	register SHORT current = 0;
	register SHORT xval, yval;
	register struct MenuItem *item;

	/* Get the x and y vals of the mouse position */

	xval = win->MouseX - SETX;
	yval = win->MouseY - SETY;

	/* If there is a title, decrement the yval by the correct amount */

	if(menu->MenuName)
		yval -= POPTITLEHEIGHT;

	/* First, see if the pointer is even in the window */

	if((xval < 0) || (yval < 0) ||
	   (xval > win->Width - SETX) || (yval > win->Height - SETY))
	   	return(-1);

	/* search through the list of menu items, checking the select box  */
	/* of each.  If containment is detected, the job is done.          */

	item = menu->FirstItem;
	while(item) {
		if((xval >= item->LeftEdge) && (yval >= item->TopEdge) &&
		   (xval <= item->LeftEdge + item->Width) &&
		   (yval <= item->TopEdge + item->Height)) {

		   	/* We have found the quarry; now, the result only  */
			/* depends on the MenuItem's ITEMENABLED flag.     */

			if(item->Flags & ITEMENABLED)
		   		return(current);
			else
				return(-1);
		}
		current++;
		item = item->NextItem;
	}

	/* If the list is exhausted, return the sad news */

	return(-1);
}

/*
 * pop_highlight()
 *
 * highlight a menu item
 */

#define pop_highlight(win, menu, state) pop_do_highlighting(win, menu, state, 0)

/*
 * pop_unhighlight()
 *
 * unhighlight a menu item
 */

#define pop_unhighlight(win, menu, state) pop_do_highlighting(win, menu, state, 1)

/*
 * pop_do_highlighting()
 *
 * Highlight or unhighlight a menu item, given its traversal number.  Assumes
 * this is a rational value -- if it isn't, Watch Out.
 */

VOID
pop_do_highlighting(win, menu, state, mode)
struct Window *win;
struct Menu *menu;
SHORT state;
SHORT mode; /* 0 means to highlight, 1 means to unhighlight */
{
	register struct MenuItem *item;
	struct RastPort *rp;
	SHORT offset = 0;

	if(menu->MenuName)
		offset = POPTITLEHEIGHT;

	/* Get the correct MenuItem structure */

	item = pop_getitem(menu, state);

	rp = win->RPort;

	/* Now, do the highlighting!  The action to be taken depends on    */
	/* the type of highlighting desired for this item.                 */
	/* The way that the flags for highlighting works is truly bizarre  */

	if((item->Flags & HIGHNONE) == HIGHNONE)
		return;

	if(item->Flags & HIGHCOMP) {
		SetDrMd(rp, COMPLEMENT);
		RectFill(rp, (LONG) item->LeftEdge + SETX, (LONG) (item->TopEdge +
		  offset + SETY), (LONG) (item->LeftEdge + item->Width - 1 + SETX),
		  (LONG) (item->TopEdge + item->Height + offset + SETY));
	}
	else if(item->Flags & HIGHBOX) {
		SetDrMd(rp, COMPLEMENT);
		Move(rp, (LONG) item->LeftEdge + SETX, (LONG) (item->TopEdge + 
		  offset + SETY));

		Draw(rp, (LONG) (item->LeftEdge + item->Width - 1 + SETX),
		  (LONG) (item->TopEdge + offset + SETY));
		Draw(rp, (LONG) (item->LeftEdge + item->Width - 1 + SETX),
		  (LONG) (item->TopEdge + item->Height + offset + SETY));
		Move(rp, (LONG) (item->LeftEdge + item->Width - 2 + SETX),
		  (LONG) (item->TopEdge + offset + 1 + SETY));
		Draw(rp, (LONG) (item->LeftEdge + item->Width - 2 + SETX),
		  (LONG) (item->TopEdge + item->Height + offset + SETY));

		Draw(rp, (LONG) item->LeftEdge + SETX,
		  (LONG) (item->TopEdge + item->Height + offset + SETY));
		Draw(rp, (LONG) item->LeftEdge + SETX, (LONG) 
		  (item->TopEdge + offset + 1 + SETY));

		Move(rp, (LONG) item->LeftEdge + 1 + SETX, (LONG) 
		  (item->TopEdge + offset + 1 + SETY));
		Draw(rp, (LONG) item->LeftEdge + 1 + SETX,
		  (LONG) (item->TopEdge + item->Height + offset - 1 + SETY));
	}

	/*  Otherwise, the mode is HIGHIMAGE */

	else
		pop_draw_menuitem(win, item, !mode, offset);
}

/*
 * pop_render()
 *
 * renders the menu title (if existent) and the menu items
 */

VOID
pop_render(win, menu)
struct Window *win;
struct Menu *menu;
{
	struct MenuItem *item;
	struct RastPort *rp;
	SHORT offset = 0;

	rp = win->RPort;

	/* Fill the background with color 1, like Intuition Menus */

	SetAPen(rp, 1L);
	RectFill(rp, 0L + SETX, 0L + SETY, (LONG) win->Width - SETX - 1, (LONG) win->Height - 2 - SETY);

	/* First, if there is a Title for this menu, render it in the top */
	/* of the menu.                                                   */

	if(menu->MenuName) {
		SetDrMd(rp, JAM1);
		SetAPen(rp, 1L);
		RectFill(rp, 0L, 0L, (LONG) win->Width - 1, (LONG) POPTITLEHEIGHT - 1);
		SetAPen(rp, 0L);
		SetBPen(rp, 1L);
		Move(rp, 4L + SETX, 7L);
		Text(rp, menu->MenuName, (LONG) pop_strlen(menu->MenuName));
		SetDrMd(rp, COMPLEMENT);
		RectFill(rp, 0L, 0L, (LONG) win->Width - 1, (LONG) POPTITLEHEIGHT - 1);
		SetDrMd(rp, JAM1);
		offset = POPTITLEHEIGHT;
		SetAPen(rp, 0);
		Move(rp, 0, POPTITLEHEIGHT);
		Draw(rp, win->Width - 1, POPTITLEHEIGHT);
	}

	/* now render all of the menu items */

	item = menu->FirstItem;
	while(item) {
		pop_draw_menuitem(win, item, 0, offset);
		item = item->NextItem;
	}
}

/*
 * pop_draw_menuitem()
 *
 * Draws the specified menuitem in the given rastport.  The mode argument
 * says what to draw -- 0 means draw the ItemFill, 1 the SelectFill.
 */

VOID
pop_draw_menuitem(win, item, mode, offset)
struct Window *win;
struct MenuItem *item;
SHORT mode;
SHORT offset;
{
	/* Area fill patterns */

	static USHORT pop_ghost_pattern[] = {
		0x1111, 0x4444
	};
	static USHORT pop_normal_pattern[] = {
		0xffff, 0xffff
	};

	APTR fill;
	struct RastPort *rp;

	/* first, figure out what to do, and return if it is a NULL thing */

	if(!mode)
		fill = item->ItemFill;
	else
		fill = item->SelectFill;

	if(!fill)
		return;

	rp = win->RPort;

	/* First, erase what may already be there, just to be sure that    */
	/* everything works out all right.                                 */

	SetAPen(rp, 1L);
	SetDrMd(rp, JAM1);
	RectFill(rp, (LONG) item->LeftEdge + SETX, (LONG) (item->TopEdge +
	  offset + SETY), (LONG) (item->LeftEdge + item->Width + SETX - 1), (LONG)
	  (item->TopEdge + item->Height + offset + SETY));

	/* If the item is checkmarked, draw the checkmark.  Intuition made */
	/* sure that the CheckMark field of the window structure exists    */

	if(item->Flags & CHECKIT)
		if(item->Flags & CHECKED)
			DrawImage(rp, win->CheckMark, (LONG)  item->LeftEdge + SETX,
			  (LONG) (item->TopEdge + offset + 1 + SETY));

	/* Now, draw the item itself -- depending on the Flag value, it    */
	/* could be either an Image or an IntuiText                        */

	if(item->Flags & ITEMTEXT)
		PrintIText(rp, fill, (LONG) item->LeftEdge + SETX, 
		  (LONG) (item->TopEdge + offset + SETY));
	else
		DrawImage(rp, fill, (LONG) item->LeftEdge + SETX, 
		  (LONG) (item->TopEdge + offset + SETY));

	/* If the ITEMENABLED flag is not set, "ghost" the item.           */

	if(!(item->Flags & ITEMENABLED)) {
		SetAPen(rp, 1L);
		SetDrMd(rp, JAM1);
		SetAfPt(rp, (USHORT *) pop_ghost_pattern, 1L);
		RectFill(rp, (LONG) item->LeftEdge + SETX, (LONG) (item->TopEdge +
		  offset + SETY), (LONG) (item->LeftEdge + item->Width + SETX - 1), (LONG)
		  (item->TopEdge + item->Height + offset + SETY));
		SetAfPt(rp, (USHORT *) pop_normal_pattern, 1L);
	}
}

/*
 * pop_getitem()
 *
 * given the traversal number of a menu item in a menu (assumes, BTW, that
 * the arguments are valid), return a pointer to the MenuItem structure
 */

struct MenuItem *
pop_getitem(menu, which)
struct Menu *menu;
SHORT which;
{
	struct MenuItem *item;

	item = menu->FirstItem;
	while(which--)
		item = item->NextItem;
	return(item);
}

/*
 * pop_strlen()
 *
 * a home-brewed strlen to prevent it being necessary to hook in whatever
 * huge object file in which the c library's strlen() resides.
 */

SHORT
pop_strlen(str)
char *str;
{
	register SHORT count = 0;

	for(; *str++; count++);
	return(count);
}

#define CONTROLBITS     ( (1 << 5) | (1 << 6) )

ULONG
InvertKeyMap(ansicode,ie,km)
ULONG ansicode;
register struct InputEvent *ie;
struct KeyMap *km;
{
	LONG kindex;
	UBYTE code = 0;

	ie -> ie_Class		= IECLASS_RAWKEY;
	ie -> ie_EventAddress	= 0;

		/* check for codes in (default) high map first  */

	switch(ansicode)
	{
		case ' ':	code = 0x40;	/* space            */
				break;

		case 0x08:      code = 0x41;	/* backspace    */
				break;

		case '\t':	code = 0x42;	/* tab                  */
				break;

		case 0x0D:	code = 0x44;	/* return               */
				break;

		case 0x1B:	code = 0x45;	/* esc                  */
				break;

		case 0x7F:	code = 0x46;	/* del                  */
				break;
	}

	ie -> ie_Code		= 0;
	ie -> ie_Qualifier	= 0;

	if(code)
	{
		ie -> ie_Code		= code;
		ie -> ie_Qualifier	= 0;

		return(TRUE);
	}

	LowKeyInvert((UBYTE)ansicode,km,&ie -> ie_Code,&ie -> ie_Qualifier,&kindex);

	if(!ie -> ie_Code && !ie -> ie_Qualifier)
		return(FALSE);

	return(TRUE);
}

#define KEYMAPSIZE      64

/* LowKeyInvert returns good code else <0 if no find
 *
 * regarding keymap as many-to-one mapping:
 * -entries for a given key are scanned so that
 *      the minimum number of qualifiers are associated
 *      with the keystroke.
 * -passing a character value of zero corresponds, in
 *      the default keymap, to CTRL-`, which is probably a bug.
 * -numerals are matched with numeric pad keystrokes (no
 *      qualifiers) on standard keymap.  The way to specify
 *      a key on the number row is via its shifted value;
 *      specify explicitly that the qualifier is to be unshifted,
 *      or a "don't care."
 */

LowKeyInvert(value,km,codep,qualp,indexp)
register UBYTE value;	/* translation value from low keymap    */
struct KeyMap *km;
UWORD *codep;		/* pointers where answers are to be put */
UWORD *qualp;
ULONG *indexp;		/* dead-key index information (put into ie?)    */
{
	register UWORD code = KEYMAPSIZE - 1;	/* last entry   */
	register unsigned int type;
	register LONG *p;		/* points to four-byte lokeymap entry   */
	int found_it = 0;

	*indexp = *qualp = 0;

	p = (LONG *)km -> km_LoKeyMap + code;

	do
	{
			/* determine type of key        */

		if((type = km -> km_LoKeyMapTypes[code]) == KC_VANILLA)
			found_it = CheckVanilla(p,value,qualp);
		else
			if(!(type & KCF_NOP))
				found_it = CheckNormal(p,value,type,qualp);

		--p;
	}
	while(!found_it && code--);

	*codep = code;

	return(code);
}

CheckNormal(four_bytesp, val, type, qualp)
LONG four_bytesp;
UBYTE val;
UWORD type;
UWORD *qualp;
{
	register UBYTE *p = (UBYTE *)four_bytesp; /* codes held in long word  */
	register long position;

		/* start with last of four bytes, "more vanilla"        */

	p += 3;

	for(position = 3 ; position >= 0 ; --position, --p)
	{
		if(*p == val)
		{
			switch(type)
			{
				case KC_NOQUAL:	if(position != 3)
							goto NOT_THIS;

						break;

				case KCF_SHIFT:	if(!(position & 2))
							goto NOT_THIS;

						if(position == 2)
							*qualp |= IEQUALIFIER_LSHIFT;

						break;

				case KCF_ALT:	if(!(position & 2))
							goto NOT_THIS;

						if(position == 2)
							*qualp |= IEQUALIFIER_LALT;

						break;


				case KCF_CONTROL:

						if(!(position & 2))
							goto NOT_THIS;

						if(position == 2)
							*qualp |= IEQUALIFIER_CONTROL;

						break;

				case KCF_ALT | KCF_CONTROL:

						if(!(position & 1))
							*qualp |= IEQUALIFIER_LALT;

						if(!(position & 2))
							*qualp |= IEQUALIFIER_CONTROL;

						break;

				case KCF_SHIFT | KCF_CONTROL:

						if(!(position & 1))
							*qualp |= IEQUALIFIER_LSHIFT;

						if(!(position & 2))
							*qualp |= IEQUALIFIER_CONTROL;

						break;


				case KCF_SHIFT | KCF_ALT:

						if(!(position & 1))
							*qualp |= IEQUALIFIER_LSHIFT;

						if(!(position & 2))
							*qualp |= IEQUALIFIER_LALT;

						break;

				default:	break;
			}

			return(TRUE);
		}
NOT_THIS:       ;
	}

	return(FALSE);
}

CheckVanilla(p,val,qualp)
UBYTE *p;		/* note: byte pointer   */
UBYTE val;
UWORD *qualp;
{
	register int i;

		/* only one way to match a vanilla control key  */

	if(!(val & CONTROLBITS))
	{
		/* is a control code    */

		if((p[3] & ~CONTROLBITS) == val)
		{
			*qualp |= IEQUALIFIER_CONTROL;
			return(TRUE);
		}
	}
	else
	{
		/* not a control        */

		for(i = 3 ; i >= 0 ; --i)
		{
			if(p[i] == val)
			{
				if(!(i & 1))
					*qualp |= IEQUALIFIER_LSHIFT;

				if(!(i & 2))
					*qualp |= IEQUALIFIER_LALT;

				return(TRUE);
			}
		}
	}

	return(FALSE);
}
