/***************************************************************************
 *									   *
 *  MODULE	: MpFind.c	    					   *
 *									   *
 *  PURPOSE	: Code to do text searches in MultiPad. 		   *
 *									   *
 *  FUNCTIONS	: RealSlowCompare () - Compares subject string with target *
 *				       string.				   *
 *									   *
 *		  MyFindText ()	     - Looks for the search string in the  *
 *				       active window.			   *
 *									   *
 *		  FindPrev ()	     - Find previous occurence of search   *
 *				       string.				   *
 *									   *
 *		  FindNext ()	     - Find next occurence of search string*
 *									   *
 *		  FindDlgProc ()     - Dialog function for Search/Find.    *
 *									   *
 *		  Find ()	     - Invokes FindDlgProc ()		   *
 *									   *
 ***************************************************************************/
#include "multipad.h"

#undef HIWORD
#undef LOWORD

#define HIWORD(l) (((WORD*)&(l))[1])
#define LOWORD(l) (((WORD*)&(l))[0])

BOOL fCase	   = FALSE;    /* Turn case sensitivity off */
char szSearch[160] = "";       /* Initialize search string  */

/****************************************************************************
 *									    *
 *  FUNCTION   : RealSlowCompare ()					    *
 *									    *
 *  PURPOSE    : Compares subject string with the target string. This fn/   *
 *		 is called repeatedly so that all substrings are compared,  *
 *		 which makes it O(n ** 2), hence it's name.                 *
 *									    *
 *  RETURNS    : TRUE  - If pSubject is identical to pTarget.		    *
 *		 FALSE - otherwise.					    *
 *									    *
 ****************************************************************************/

BOOL NEAR PASCAL RealSlowCompare (pSubject, pTarget )
register PSTR pSubject;
register PSTR pTarget;
{
    if (fCase){
	while (*pTarget)
	    if (*pTarget++ != *pSubject++)
		return FALSE;
    }
    else{
	/* If case-insensitive, convert both subject and target to lowercase
	 * before comparing.
	 */
	AnsiLower ((LPSTR)pTarget);
	while (*pTarget)
	    if (*pTarget++ != (char)(DWORD)AnsiLower ((LPSTR)(DWORD)(BYTE)*pSubject++))
		return FALSE;
    }
    return TRUE;
}

/****************************************************************************
 *									    *
 *  FUNCTION   : MyFindText ()						    *
 *									    *
 *  PURPOSE    : Finds the search string in the active window according to  *
 *		 search direction (dch) specified ( -1 for reverse and 1 for*
 *		 forward searches).					    *
 *									    *
 ****************************************************************************/
VOID NEAR PASCAL MyFindText( dch )
register int dch;

{
    register PSTR pText;
    HANDLE	  hT;
    LONG	  l;
    WORD	  cch;
    int 	  i;

    if (!*szSearch)
	return;

    /* Find the current selection range */
    l = SendMessage(hwndActiveEdit, EM_GETSEL, 0, 0L);

    /* Get the handle to the text buffer and lock it */
    hT	  = (HANDLE)SendMessage (hwndActiveEdit, EM_GETHANDLE, 0, 0L);
    pText = LocalLock(hT);

    /* Get the length of the text */
    cch = (WORD)SendMessage (hwndActiveEdit, WM_GETTEXTLENGTH, 0, 0L);

    /* Start with the next char. in selected range ... */
    pText += LOWORD (l) + dch;

    /* Compute how many characters are before/after the current selection*/
    if (dch < 0)
	i = LOWORD (l);
    else
	i = cch - LOWORD (l) + 1 - lstrlen (szSearch);

    /* While there are uncompared substrings... */
    while ( i > 0){
	LOWORD(l)+=dch;

	/* Does this substring match? */
	if (RealSlowCompare(pText,szSearch)){

	    /* Yes, unlock the buffer.*/
	    LocalUnlock(hT);

	    /* Select the located string */
	    HIWORD(l) = LOWORD(l) + lstrlen (szSearch);
	    SendMessage (hwndActiveEdit, EM_SETSEL, 0, l);
	    return;
	}
	i--;

	/* increment/decrement start position by 1 */
	pText += dch;
    }

    /* Not found... unlock buffer. */
    LocalUnlock (hT);

    /* Give a message */
    MPError (hwndFrame, MB_OK | MB_ICONEXCLAMATION, IDS_CANTFIND, (LPSTR)szSearch);

    return;
}

/****************************************************************************
 *									    *
 *  FUNCTION   : FindPrev ()						    *
 *									    *
 *  PURPOSE    : Finds the previous occurence of the search string. Calls   *
 *		 MyFindText () with a negative search direction.		    *
 *									    *
 ****************************************************************************/
VOID FAR PASCAL FindPrev(void)
{
    MyFindText(-1);
}

/****************************************************************************
 *									    *
 *  FUNCTION   : FindNext ()						    *
 *									    *
 *  PURPOSE    : Finds the next occurence of search string. Calls	    *
 *		 MyFindText () with a positive search direction.		    *
 *									    *
 ****************************************************************************/
VOID FAR PASCAL FindNext(void)
{
    MyFindText(1);
}

/****************************************************************************
 *									    *
 *  FUNCTION   : FindDlgProc(hwnd, message, wParam, lParam)		    *
 *									    *
 *  PURPOSE    : Dialog function for the Search/Find command. Prompts user  *
 *		 for target string, case flag and search direction.	    *
 *									    *
 ****************************************************************************/
BOOL FAR PASCAL FindDlgProc(hwnd, msg, wParam, lParam)
HWND hwnd;
WORD msg;
WORD wParam;
LONG lParam;
{
    switch (msg){
	case WM_INITDIALOG:{

	    /* Check/uncheck case button */
	    CheckDlgButton (hwnd, IDD_CASE, fCase);

	    /* Set default search string to most recently searched string */
	    SetDlgItemText (hwnd, IDD_SEARCH, szSearch);

	    /* Allow search only if target is nonempty */
	    if (!lstrlen (szSearch)){
		EnableWindow (GetDlgItem (hwnd, IDOK), FALSE);
		EnableWindow (GetDlgItem (hwnd, IDD_PREV), FALSE);
	    }
	    break;
	}

	case WM_COMMAND:{

	    /* Search forward by default (see IDOK below) */
	    int i = 1;

	    switch (wParam){
		/* if the search target becomes non-empty, enable searching */
		case IDD_SEARCH:
		    if (HIWORD (lParam) == EN_CHANGE){
			if (!(WORD) SendDlgItemMessage (hwnd,
							IDD_SEARCH,
							WM_GETTEXTLENGTH,
							0,
							0L))
			    i = FALSE;
			else
			    i = TRUE;
			EnableWindow (GetDlgItem (hwnd, IDOK), i);
			EnableWindow (GetDlgItem (hwnd, IDD_PREV), i);
		    }
		    break;

		case IDD_CASE:
		    /* Toggle state of case button */
		    CheckDlgButton (hwnd,
				    IDD_CASE,
				    !IsDlgButtonChecked (hwnd, IDD_CASE));
		    break;

		case IDD_PREV:
		    /* Set direction to backwards */
		    i=-1;
		    /*** FALL THRU ***/

		case IDOK:
		    /* Save case selection */
		    fCase = IsDlgButtonChecked( hwnd, IDD_CASE);

		    /* Get search string */
		    GetDlgItemText (hwnd, IDD_SEARCH, szSearch, sizeof (szSearch));

		    /* Find the text */
		    MyFindText (i);
		    /*** FALL THRU ***/

		/* End the dialog */
		case IDCANCEL:
		    EndDialog (hwnd, 0);
		    break;

		default:
		    return FALSE;
	    }
	    break;
	}
	default:
	    return FALSE;
    }
    return TRUE;
}

/****************************************************************************
 *									    *
 *  FUNCTION   : Find() 						    *
 *									    *
 *  PURPOSE    : Invokes the Search/Find dialog.			    *
 *									    *
 ****************************************************************************/

VOID FAR PASCAL Find()
{
    FARPROC lpfn;

    lpfn = MakeProcInstance (FindDlgProc, hInst);
    DialogBox (hInst, IDD_FIND, hwndFrame, lpfn);
    FreeProcInstance (lpfn);
}
