/****************************************************************************
** PROJECT  **                           LOCATE                            **
*****************************************************************************
** FUNCTION ** LOCATE : A HIGHLY USEFUL (?) MACRO TO REPLACE THE DEFAULT   **
**          ** BRIEF SEARCH MACROS. INSTEAD OF IMPLEMENTING A GOTO         **
**          ** PREVIOUS/NEXT SYSTEM, THIS MACRO WILL FIND ALL OCCURENCES   **
**          ** OF THE SUPPLIED STRING/REGULAR EXPRESSION AND DISPLAY THEM  **
**          ** WITHIN A POP-UP MENU. THE USER MAY THEN USE THE CURSOR KEYS **
**          ** TO PICK THE DESIRED LINE AND GOTO IT BY PRESSING <Return>   **
**          **                                                             **
**          ** FINDWORD : A (VERY) BRIEF MACRO THAT WILL "LOCATE" ALL      **
**          ** OCCURENCES OF THE PHRASE THAT IS UNDER THE CURSOR AS IT IS  **
**          ** INVOKED                                                     **
*****************************************************************************
**  AUTHOR  ** WARREN D HUMPHREYS                                          **
*****************************************************************************
**   DATE   ** FRIDAY 30TH NOVEMBER 1990                                   **
*****************************************************************************
** COMMENTS ** RECOMMENDED KEY ASSOCIATIONS ARE :- <F12>       Locate      **
**          **                                     <Alt-F12>   L_Toggle_GS **
**          **                                     <Ctrl-F12>  L_Toggle_RE **
**          **                                     <Shift-F12> L_Toggle_CS **
**          **                                     <Alt-S>     FindWord    **
**          **                                                             **
**          **                                                             **
**          ** TO INSTALL THIS FROM YOUR INITIALS MACRO, THE FOLLOWING     **
**          ** LINE SHOULD BE USED :-                                      **
**          **                                                             **
**          ** (autoload "LOCATE" "Locate" "FindWord" "L_Toggle_CS"        **
**          **   "L_Toggle_GS" "L_Toggle_RE" "_save_state" "_restore")     **
**          **                                                             **
**          ** FOLLOWED BY THE DESIRED KEY ASSOCIATIONS. IF THE ABOVE KEY  **
**          ** ASSOCIATIONS ARE TO BE USED, THE COMMANDS TO ACHIEVE THEM   **
**          ** ARE :-                                                      **
**          **                                                             **
**          ** (assign_to_key "<F12>" "Locate")                            **
**          ** (assign_to_key "<Alt-S>" "FindWord")                        **
**          ** (assign_to_key "<Shift-F12>" "L_Toggle_CS")                 **
**          ** (assign_to_key "<Alt-F12>" "L_Toggle_GS")                   **
**          ** (assign_to_key "<Ctrl-F12>" "L_Toggle_RE")                  **
**          **                                                             **
*****************************************************************************
**                          MODIFICATION HISTORY                           **
*****************************************************************************
**   DATE   ** WHO **                     CHANGED WHAT                     **
*****************************************************************************
** 29/12/90 ** WDH ** CHANGED BOTH L_Toggle_CS & L_Toggle_RE SO THAT THEY  **
**          **     ** CAN BE CALLED FROM ANOTHER MACRO AND PASSED A STATE  **
**          **     ** THEY ALSO NOW RETURN THEIR PREVIOUS STATE            **
** 26/02/91 ** WDH ** FIXED BUG THAT CAUSED LOCATE TO ENTER AN INFINITE    **
**          **     ** LOOP WHEN LOCATING WITHIN A MARKED BLOCK             **
** 08/03/91 ** WDH ** ADDED NEW FEATURE : PREVIOUSLY GOING TO A LINE LEFT  **
**          **     ** THE CURSOR AT THE BEGINNING OF THE LINE. IT NOW      **
**          **     ** LEAVES THE CURSOR ON THE FIRST CHARACTER OF THE      **
**          **     ** FIRST OCCURENCE OF THE SEARCH PATTERN ON THAT LINE   **
** 16/05/91 ** WDH ** CHANGED NAME OF SEARCH PATTERN VARIABLE FROM         **
**          ** WDH ** "SearchPat" TO "LocatePat"                           **
** 06/06/91 ** WDH ** INCORPORATED "FINDWORD" WITH A FEW ALTERATIONS       **
** 05/08/91 ** WDH ** FIXED BUG THAT MEANT THAT THE WINDOW WOULD NOT OPEN  **
**          **     ** IF THE SUPPLIED SEARCH PATTERN CAUSED THE MESSAGE IN **
**          **     ** THE BOTTOM LINE TO BE SO LONG AS TO BE WIDER THAN    **
**          **     ** THE ACTUAL WINDOW, CAUSING IT TO REFUSE TO OPEN.     **
** 06/09/91 ** WDH ** ADDED CTRL_BREAK HANDLING :- IF CTRL_BREAK IS HIT    **
**          **     ** DURING SEARCHING, THE SEARCH IS TERMINATED AT THAT   **
**          **     ** POINT AS IF NO MORE ENTRIES CAN BE FOUND.            **
**          **     ** ALSO CHANGED NAME OF "L_Goto" TO "_L_Goto" SO AS TO  **
**          **     ** PREVENT IT FROM BEING CALLED VIA THE KEYBOARD.       **
** 09/09/91 ** WDH ** ADDED STATE SAVING CAPABILITY :- THE SEARCH PATTERN, **
**          **     ** CASE SENSITIVITY FLAG AND REGULAR EXPRESSIONS FLAG   **
**          **     ** ARE ALL SAVED IN THE STATE FILE.                     **
**          **     ** NOW SUPPORTS THE LOCATION OF THE SUPPLIED STRING OR  **
**          **     ** REGULAR EXPRESSION WITHIN A MARKED BLOCK.            **
**          **     ** THIS FAILED TO WORK IN PREVIOUS VERSIONS DUE TO A    **
**          **     ** MISUNDERSTANDING OF HOW "SEARCH_FWD" WORKED WITH A   **
**          **     ** MARKED BLOCK.                                        **
** 13/09/91 ** WDH ** FIXED A SILLY BUG INTODUCED IN THE LASTEST REVISION. **
**          **     ** WHEN A MARKED BLOCK WAS SEARCHED, AND A LINE OTHER   **
**          **     ** THAN THE FIRST ONE FOUND WAS SELECTED TO GOTO, THE   **
**          **     ** LINE THAT LOCATE ACTUALLY JUMPED TO WAS ALWAYS THE   **
**          **     ** FIRST ONE THAT HAD BEEN LOCATED. DUE TO INCORRECT    **
**          **     ** USE OF THE SEARCH_FWD FUNCTION.                      **
** 26/09/91 ** WDH ** NOW DISPLAYS MESSAGE AS SOON AS SEARCH STARTS. WHEN  **
**          **     ** DOING COMPLICATED SEARCHES ON LARGE BUFFERS, BEFORE  **
**          **     ** THE FIRST "X OCCURENCES FOUND IN X LINES" MESSAGE    **
**          **     ** WAS DISPLAYED, IT APPEARED THAT NOTHING WAS BEING    **
**          **     ** LOCATED.                                             **
** 18/10/91 **     ** SLIGHT MODIFICATION. THE MESSAGE DISPLAYED AT THE    **
**          **     ** BOTTOM OF THE SCREEN WHILE LOCATING THE PATTERN USED **
**          **     ** TO ALWAYS DISPLAY "N Occurences located in N lines". **
**          **     ** THIS WAS ALWAYS DISPLAYED EVEN IF THERE WAS ONLY     **
**          **     ** ONE OCCURENCE IN 1 LINE. THE DISTINCTION BETWEEN ONE **
**          **     ** AND MORE THAN ONE OCCURENCE/LINE IS NOW MADE.        **
** 18/11/91 ** WDH ** JUST A MINOR CHANGE! ADDED GLOBAL SEARCH FACILITY.   **
**          **     ** IT WAS A BIT OF A PAIN TO ADD, BUT IT'S HERE. THE    **
**          **     ** ABILITY TO SEARCH ALL THE BUFFERS CURRENTLY LOADED   **
**          **     ** MAKES LOCATE EVEN MORE USEFUL (?)                    **
**          **     ** ASSIGNED GLOBAL SEARCH TOGGLE TO <Alt-F12>. THIS     **
**          **     ** MEANS THAT "FindWord" HAS TO GO ON ANOTHER KEY. AS I **
**          **     ** NO LONGER USE THE DEFAULT SEARCH MACROS, I MOVED IT  **
**          **     ** TO <Alt-S> ON MY KEYBOARD, BUT OTHER USERS MAY NOT   **
**          **     ** FIND THIS ENTIRELY SUITABLE.                         **
****************************************************************************/

#include	<dialog.h>

#define		BREAKMSG				".."
#define		fw_BeginDelim		"[ ();:=+@^<>{}*,$.\\n\\t'\"(]+\\c[~ \\t]"
#define		fw_EndDelim			"[ ();:=+@^<>{}*,$\\t\\n\\)\\'\\.\"\\\/\\,\\\\]"
#define		HBORDERGAP			1
#define		MAXMSGLEN				70
#define		MOREPATMSG			".."
#define		VBORDERGAP			3

/****************************************************************************
**                           FUNCTION PROTOTYPES                           **
****************************************************************************/

void		_init();
int			_L_Goto(int,...);
int			_L_Ctrl_Break(void);
int			FindWord();
int			Locate(~string);
int			L_Toggle_CS(~int);
int			L_Toggle_GS(~int);
int			L_Toggle_RE(~int);
extern	_remove_newlines();
replacement	_restore();
replacement	_save_state();

/****************************************************************************
**                            GLOBAL VARIABLES                             **
****************************************************************************/

int			L_CSState;
int			L_Ctrl_Break_State;
int			L_GSState;
int			L_REState;
int			PickLine;

string	LocatePat,
				PickBuff;

/****************************************************************************
**                            ACTUAL FUNCTIONS                             **
****************************************************************************/

void		_init()
{
	L_CSState=FALSE;
	L_GSState=FALSE;
	L_REState=FALSE;
}


int			Locate(~string)
{
	int			BlockSL,BlockSC,BlockEL,BlockEC,
					BuffCount,
					FirstBuff,
					Found,
					FoundCount,
					FoundLine,
					InBlock,
					InputBuffer,
					LastBuff,
					LastLine,
					LineCount,
					MarkState,
					MaxPatLen,
					OldMsgLvl,
					OutputBuffer,
					ReturnCode,
					ScreenRows,ScreenCols,
					WinTop,WinBot,WinLeft,WinRight;

	string	BuffExt,
					BuffName,
					FoundText,
					LocateDispPat,
					PostPatMsg,
					PrePatMsg,
					Prompt,
					WinMsg;

	OldMsgLvl=set_msg_level(1);

	sprintf(Prompt," Locate (CS=%s,GS=%s,RE=%s) ? : ",
		((L_CSState) ? "on" : "off"),
		((L_GSState) ? "on" : "off"),
		((L_REState) ? "on" : "off"));

	if (get_parm(0,LocatePat,Prompt,NULL,LocatePat))
	{
		message("Locating %s...",LocatePat);
		L_Ctrl_Break_State=0;
		BuffCount=0;
		FoundCount=0;
		PickLine=0;
		LastBuff=0;
		LastLine=0;

		register_macro(7,"_L_Ctrl_Break");
		FirstBuff=inq_buffer(NULL);
		InputBuffer=inq_buffer();
		OutputBuffer=create_buffer("Locate",NULL,1);

		save_position();
		MarkState=inq_marked(BlockSL,BlockSC,BlockEL,BlockEC);
		InBlock=TRUE;

		do
		{
			inq_names(NULL,BuffExt,BuffName);
			BuffName=upper(substr(BuffName,1,rindex(BuffName,".")-1));
			BuffExt=upper(BuffExt);

			if ((MarkState) && (!L_GSState))
				move_abs(BlockSL,BlockSC);
			else
				top_of_buffer();

			do
			{
				if ((Found=search_fwd(LocatePat,L_REState,L_CSState,0,NULL)) > 0)
				{
					inq_position(FoundLine,NULL);
					if (MarkState)
						InBlock=(FoundLine <= BlockEL);

					if (InputBuffer!=LastBuff)
						BuffCount+=1;

					if (InBlock)
					{
						FoundCount+=1;
						if (FoundLine!=LastLine)
						{
							LineCount+=1;
							save_position();
							move_abs(0,1);
							FoundText=trim(ltrim(read()));
							restore_position();
							set_buffer(OutputBuffer);
							if (L_GSState)
								insert("\n%0.8s.%0.3s(%05d):%04d  %s",BuffName,BuffExt,
									InputBuffer,FoundLine,FoundText);
							else
								insert("\n%04d  %s",FoundLine,FoundText);
							set_buffer(InputBuffer);
							LastLine=FoundLine;
						}
					}
					next_char();
					if (L_GSState)
						message("%d Occurence%s located in %d line%s of %d buffer%s",
							FoundCount,FoundCount > 1 ? "s" : "",LineCount,
							LineCount > 1 ? "s" : "",BuffCount,BuffCount > 1 ? "s" : "");
					else
						message("%d Occurence%s located in %d line%s",FoundCount,
						FoundCount > 1 ? "s" : "",LineCount,LineCount > 1 ? "s" : "");

					LastBuff=InputBuffer;
				}
			}
			while ((Found > 0) && (InBlock) && (!L_Ctrl_Break_State));

			if (L_GSState)
				set_buffer(InputBuffer=next_buffer(0));
		}
		while ((InputBuffer!=FirstBuff) && (!L_Ctrl_Break_State) && (L_GSState));

		restore_position();

		if (FoundCount==0)
		{
 			message("Pattern not located%s.",L_Ctrl_Break_State ? " so far" : "");
			ReturnCode=FALSE;
		}
		else
		{
			set_buffer(OutputBuffer);
			move_abs(1,1);
			delete_line();
			set_buffer(InputBuffer);
			inq_screen_size(ScreenRows,ScreenCols);
			ScreenRows-=1;
			if (FoundCount > (ScreenRows-((VBORDERGAP+1)*2)))
			{
				WinTop=(VBORDERGAP+1);
				WinBot=(ScreenRows-(VBORDERGAP+2));
			}
			else
			{
				WinTop=(((ScreenRows-LineCount)/2)-1);
				WinBot=(WinTop+LineCount+1);
			}
			WinLeft=(HBORDERGAP+1);
			WinRight=(ScreenCols-(HBORDERGAP+2));

			PickLine=0;

			sprintf(PrePatMsg,"%d Occurence%s of \"",FoundCount,FoundCount > 1 ?
				"s" : "");
			if (L_GSState)
				sprintf(PostPatMsg,"\" located in %d line%s of %d buffer%s%s",LineCount,
					(LineCount==1) ? "" : "s",BuffCount,(BuffCount==1) ? "" : "s",
					L_Ctrl_Break_State ? BREAKMSG : "");
			else
				sprintf(PostPatMsg,"\" located in %d line%s%s",LineCount,
					(LineCount==1) ? "" : "s",L_Ctrl_Break_State ? BREAKMSG : "");

			MaxPatLen=(MAXMSGLEN-strlen(PrePatMsg)-strlen(PostPatMsg));
			if (strlen(LocatePat) > MaxPatLen)
				MaxPatLen-=strlen(MOREPATMSG);

			sprintf(LocateDispPat,"%s%s",substr(LocatePat,1,MaxPatLen),
				(strlen(LocatePat) > MaxPatLen) ? MOREPATMSG : "");

			sprintf(WinMsg,"%s%s%s",PrePatMsg,LocateDispPat,PostPatMsg);

			_process_menu(WinLeft,WinBot,WinRight,WinTop,NULL,WinMsg,NULL,
				OutputBuffer,"_L_Goto",1);

			if (PickLine!=0)
			{
				if (PickBuff!="")
					edit_file(PickBuff);
				goto_line(PickLine);
				search_fwd(LocatePat,L_REState,L_CSState,NULL,NULL);
			}
			delete_buffer(OutputBuffer);
			unregister_macro(7,"_L_Ctrl_Break");
			ReturnCode=TRUE;
		}
	}
	set_msg_level(OldMsgLvl);
	return(ReturnCode);
}


int			_L_Ctrl_Break(void)
{
	L_Ctrl_Break_State=1;
	return(0);
}

int			_L_Goto(int Event_Type,...)
{
	int			BuffHandle;

	string	Item_Text,
					BuffToFind;

	get_parm(0,Event_Type);

	switch (Event_Type)
	{
		case DIALOG_PICK_MENU	:
		case DIALOG_F10				:
		{
			get_parm(2,Item_Text);
			if (L_GSState)
			{
				BuffHandle=set_buffer(atoi(substr(Item_Text,14,5)));
				inq_names(PickBuff,NULL,NULL);
				PickLine=(atoi(substr(Item_Text,21,4)));
			}
			else
			{
				PickBuff="";
				PickLine=(atoi(substr(Item_Text,1,4)));
			}
			_dialog_esc();
		}
	}
	return(TRUE);
}


int			L_Toggle_CS(~int)
{
	int		OldState=L_CSState;

	if (!get_parm(0,L_CSState))
	{
		L_CSState=!L_CSState;
		message("Case sensitivity for Locate is now %s.",(L_CSState) ? "on" :
			"off");
	}
	return(OldState);
}

int			L_Toggle_GS(~int)
{
	int		OldState=L_GSState;

	if (!get_parm(0,L_GSState))
	{
		L_GSState=!L_GSState;
		message("Global Searching for Locate is now %s.",(L_GSState) ? "on" :
			"off");
	}
	return(OldState);
}

int			L_Toggle_RE(~int)
{
	int		OldState=L_REState;

	if (!get_parm(0,L_REState))
	{
		L_REState=!L_REState;
		message("Regular expressions for Locate are now %s.",(L_REState) ? "on" :
			"off");
	}
	return(OldState);
}

int			FindWord()
{
	int		fw_OldCSState,
				fw_OldMsgLvl,
				fw_OldREState,
				fw_ReturnCode;

	string	fw_LocatePat,
					fw_ReadBuf;

	fw_OldMsgLvl=set_msg_level(1);
	fw_OldCSState=L_Toggle_CS(0);
	fw_OldREState=L_Toggle_RE(0);

	save_position();

	search_back(fw_BeginDelim,1,0,0);

	fw_LocatePat=read();
	fw_LocatePat=substr(fw_LocatePat,1,
		search_string(fw_EndDelim,fw_LocatePat,NULL,1,0)-1);

	restore_position();

	fw_ReturnCode=Locate(fw_LocatePat);

	L_Toggle_RE(fw_OldREState);
	L_Toggle_CS(fw_OldCSState);
	set_msg_level(fw_OldMsgLvl);
	return(fw_ReturnCode);
}


replacement	_save_state()
{
	insert("[Locate]\n");
	insert("LocatePat=%s",_remove_newlines(LocatePat));
	insert("LocateCS=%s\n",L_CSState ? "ON" : "OFF");
	insert("LocateGS=%s\n",L_GSState ? "ON" : "OFF");
	insert("LocateRE=%s\n",L_REState ? "ON" : "OFF");
	return(_save_state());
}


replacement	_restore()
{
	string	StateLine;

	top_of_buffer();
	if (search_fwd("LocatePat=\\c",1,NULL,NULL,NULL) > 0)
		LocatePat=compress(ltrim(trim(read())));

	top_of_buffer();
	if (search_fwd("LocateCS=\\c",1,NULL,NULL,NULL) > 0)
	{
		StateLine=upper(compress(ltrim(trim(read()))));
		L_CSState=(StateLine=="ON");
	}
	top_of_buffer();
	if (search_fwd("LocateGS=\\c",1,NULL,NULL,NULL) > 0)
	{
		StateLine=upper(compress(ltrim(trim(read()))));
		L_GSState=(StateLine=="ON");
	}
	top_of_buffer();
	if (search_fwd("LocateRE=\\c",1,NULL,NULL,NULL) > 0)
	{
		StateLine=upper(compress(ltrim(trim(read()))));
		L_REState=(StateLine=="ON");
	}
	return(_restore());
}
