/*** mhevt - help extension event handling code
*
*   Copyright <C> 1988, Microsoft Corporation
*
*  This file contains the code called by the edit in response to events
*
* Revision History (most recent first):
*
*   30-Mar-1989 ln  Fudge with keyevent to react corectly to what we want.
*   23-Mar-1989 ln  Created. Extracted from mhcore & others
*
*************************************************************************/
#include <string.h>                     /* string functions             */
#include <malloc.h>
#include "mh.h"                         /* help extension include file  */

/*************************************************************************
*
* static data
*/
static	EVT EVThlp	= {		/* keyboard event definition	*/
			   EVT_KEY,
			   keyevent,
			   0,
			   0,
			   0		/* ALL keys			*/
			  };
static	EVT EVTcan	= {		/* cancel event definition	*/
			   EVT_CANCEL,
			   CloseWin,
			   0,
			   0,
			   0
			  };
static	EVT EVTxit	= {		/* exit event definition	*/
			   EVT_EXIT,
			   CloseWin,
			   0,
			   0,
			   0
			  };
static	EVT EVTidl	= {		/* idle event definition	*/
			   EVT_IDLE,
			   IdleProc,
			   0,
			   0,
			   0
			  };
static	EVT EVTfcs	= {		/* focus loss event definition	*/
			   EVT_LOSEFOCUS,
			   LooseFocus,
			   0,
			   0,
			   0
			  };

/*** mhevtinit - init editor event handling
*
* Input:
*  none
*
* Output:
*  Returns nothing
*
*************************************************************************/
void pascal near mhevtinit (void) {

EVTidl.focus = EVThlp.focus = pHelp;
RegisterEvent(&EVThlp); 	    /* register help key event	    */
RegisterEvent(&EVTcan); 	    /* register help cancel event   */
RegisterEvent(&EVTidl); 	    /* register help idle event     */
RegisterEvent(&EVTxit); 	    /* register help exit event     */
RegisterEvent(&EVTfcs); 	    /* register help focus event    */

/* end mhevtinit */}

/*** keyevent - called by editor whenever a key is pressed in a help window
*
*  When called we know that pHelp is being displayed, and was current.
*  Process the key pressed by the user. Keys handled:
*
*   TAB 	- move forward to next hot spot
*   BACK-TAB	- move backward to next hot spot
*   lc Alpha	- move forward to next hot spot whose text begins with alpha
*   uc Alpha	- move backward to next hot spot whose text begins with alpha
*   Enter	- execute cross reference, if we're on one
*   Space	- execute cross reference, if we're on one
*
* Input:
*  parg		= pointer to event arguments
*
* Output:
*  If the key pressed is one we recognize return TRUE, else we return FALSE
*  and let the editor process the key.
*
*************************************************************************/
flagType pascal EXTERNAL keyevent (
	EVTargs far *parg
	) {

	uchar	c;						/* character hit		*/
	int 	fDir;					/* direction flag		*/
	f		fWrapped	= FALSE;	/* wrapped arounf flag	*/
	hotspot hsCur;					/* hot spot definition	*/
	char	*pText		= NULL;
	COL 	x;
	LINE	y;

	c = parg->arg.key.KeyData.Ascii;

	//
	// if there is no topic, no sense doing anything
	//
	if (pTopic == 0) {
		if ( ((c <= 'z') && (c >= 'a')) ||
			 ((c <= 'Z') && (c >= 'A')) ||
			 (c == 0x09) ) {
			return TRUE;
		}
		return FALSE;
	}

	//
	// start by getting this info, in case it is used later.
	//
	GetTextCursor(&x, &y);
	hsCur.line = (ushort)++y;
	hsCur.col = (ushort)++x;

	//
	// If he hit return or space, look for a cross reference at the current loc.
	// If there is one, process it.
	//
	if ((c == 0x0d) || (c == ' ')) {
		if (pText = HelpXRef (pTopic, &hsCur)) {
#ifdef DEBUG
			debmsg ("Xref: ");
			if (*pText) {
				debmsg (pText);
			} else {
				debmsg ("@Local 0x");
				debhex ((long)*(ushort far *)(pText+1));
			}
			debend (TRUE);
#endif
			if (!fHelpCmd (  pText		/* command/help to look up	*/
							, FALSE		/* change focus to help window	*/
							, FALSE		/* not pop-up			*/
							)) {
				errstat ("Cannot Process Cross Reference", NULL);
			}
		}
		Display();		// Show CUrsor Position
		return TRUE;
	}

    if ( parg->arg.key.KeyData.Flags & (FLAG_CTRL  | FLAG_ALT) ) {
        return FALSE;
    }

    //
	// Maneuvering keys:
	//	TAB:		Move to next hot spot
	//	SHIFT+TAB	Move to previous hot spot
	//	lcase alpha Move to next hot spot beginning with alpha
	//	ucase alpha Move to previous hot spot beginning with alpha
	//
	if ((c <= 'z') && (c >= 'a')) {
		fDir = (int)c-0x20;
	} else if ((c <= 'Z') && (c >= 'A')) {
		fDir = -(int)c;
	} else if (c == 0x09) {
		if (parg->arg.key.KeyData.Flags & FLAG_SHIFT) {
			fDir = -1;
		} else {
			fDir = 0;
		}
	} else {
		return FALSE;
	}

	//
	// loop looking for the next cross reference that either follows or precedes
	// the current cursor position. Ensure that we do NOT end up on the same xref
	// we are currently on. If we've reached the end/beginning of the topic, wrap
	// around to the begining/end. Ensure we do this only ONCE, in case there are
	// NO cross references at all.
	//
	while (TRUE) {

		if (HelpHlNext(fDir,pTopic,&hsCur)) {

			MoveCur((COL)hsCur.col-1,(LINE)hsCur.line-1);
			IdleProc(parg);
			Display();

			if (fWrapped || ((LINE)hsCur.line != y)) {
				break;
			}

			if ((fDir < 0) && ((COL)hsCur.ecol >= x)) {
				hsCur.col--;
			} else if ((fDir >= 0) && ((COL)hsCur.col <= x)) {
				hsCur.col = (ushort)(hsCur.ecol+1);
			} else {
				break;
			}
		} else {
			if (fWrapped++) {
				break;
			}
			hsCur.col = 1;
			hsCur.line = (fDir < 0) ? (ushort)FileLength(pHelp) : (ushort)1;
		}
	}

	return TRUE;
}

/*** IdleProc - Idle event processor
*
* Purpose:
*
* Input:
*  Editor event args passed, but ignored.
*
* Output:
*  Returns .....
*
*************************************************************************/
flagType pascal EXTERNAL IdleProc (
	EVTargs far *arg
	) {

	hotspot hsCur;				/* hot spot definition		*/
	fl		flCur;				/* current cursor location	*/

	UNREFERENCED_PARAMETER( arg );

	/*
	** if there is no topic, no sense doing anything
	*/
	if (pTopic) {
		/*
		** If the cursor position has changed since the last idle call...
		*/
		GetTextCursor(&flCur.col, &flCur.lin);
		if ((flCur.col != flIdle.col) || (flCur.lin != flIdle.lin)) {
			/*
			** restore the color to the previous line, and check for a cross reference at
			** the current position. If there is one, change it's colors.
			*/
			if (flIdle.lin != -1)
				PlaceColor (flIdle.lin, 0, 0);

			hsCur.line = (ushort)(flCur.lin+1);
			hsCur.col  = (ushort)(flCur.col+1);

			if (HelpXRef (pTopic, &hsCur))
				SetColor (pHelp, flCur.lin, hsCur.col-1, hsCur.ecol-hsCur.col+1, C_WARNING);

			flIdle = flCur;
		}
	}
	Display();
	return FALSE;
}

/*** LooseFocus - called when help file looses focus
*
*  This is called each time a file looses focus. If the help file is no
*  longer displayed, we clear it from memory and deallocate any associated
*  help text.
*
* Input:
*  e		- ignored
*
* Output:
*  Returns TRUE.
*
*************************************************************************/
flagType pascal EXTERNAL LooseFocus (
EVTargs far *e
) {

UNREFERENCED_PARAMETER( e );

if (!fInPopUp && pTopic && !fInOpen) {
/*
** Look for a window that has the help file in it. If found, we're done.
*/
    if (FindHelpWin (FALSE))
	return FALSE;
/*
** There is no help window currently displayed, deallocate any topic text
** we have lying around.
*/
    if (pTopic) {
        free (pTopic);
		pTopic = NULL;
	}
/*
** If there is a help pFile, discard it's contents
*/
    if (pHelp)
	DelFile (pHelp);
    }
return TRUE;
/* end LooseFocus */}

/*** CloseWin - Close a window on the help file
*
*  Closes the help window, if it is up. Maintains window currancy after the
*  close. Relies on an eventual call to LooseFocus (above) to deallocate the
*  topic text, if it is there, and discard the help pFile.
*
*  Can be called by editor event processor, in response to CANCEL or EXIT
*  event.
*
* Input:
*  dummy	- EVTargs ignored.
*
* Output:
*  Returns TRUE.
*
*************************************************************************/
flagType pascal EXTERNAL CloseWin (
	EVTargs far *dummy
	) {


#if defined(PWB)
	/*
	** Look for the window that has the help file in it. If found, close it.
	*/
	if (pWinHelp) {
		if (!CloseWnd (pWinHelp)) {
			return TRUE;
		}

#else

	PWND	pWinCur;			/* window on entry		*/

	UNREFERENCED_PARAMETER( dummy );
	/*
	** Look for the window that has the help file in it. If found, close it.
	*/
	if (pWinHelp) {
		SetEditorObject (RQ_WIN_CUR | 0xff, pWinHelp, 0);
		if (fSplit) {
			fExecute ("meta window");
		} else {
			fExecute ("setfile");
		}
		GetEditorObject (RQ_WIN_HANDLE, 0, &pWinCur);

#endif

		pWinHelp = 0;
		if (pWinUser) {
			SetEditorObject (RQ_WIN_CUR | 0xff, pWinUser, 0);
		}
    }
	return TRUE;
}