/** Editor ED - Modul FR: Suchen und Ersetzen **/

#include "top.h"

#ifdef BUILD_FR

#include <windows.h>
#include "ut.h"
#include "res.h"

#ifdef BUILD_RE

#error Die Suche sollte besser mit RICHEDIT-eigener Funktion implementiert werden!

#else

#define MAX_STRING_SIZE 256

HWND fr_hDlg;
static FINDREPLACE fr_dlgStruc;
static TCHAR fr_findStr[MAX_STRING_SIZE], fr_replStr[MAX_STRING_SIZE];
static int fr_findCt, fr_replCt, fr_focusLost;

#ifdef BUILD_FR_PR

/** Function: Besetze Suchmuster in Suchen-Ersetzen-Dialogbox mit markiertem Text vor **/
static void fr_preset(HWND hWinEdit)
  {
  int selLen;
  TCHAR *sel;
  if ((sel=ut_getEditSel(hWinEdit, &selLen)) == NULL)
    return;
  if (selLen >= MAX_STRING_SIZE)
    selLen = MAX_STRING_SIZE - 1;
  lstrcpyn(fr_findStr, sel, selLen+1); // Im Unterschied zu strncpy kopiert
  GlobalFree(sel); // lstrcpyn max. selLen-1 Zeichen, um NUL mitanzuhaengen!
  }

#endif

/** Function: Rufe Suchen-Ersetzen-Dialogbox auf **/
HWND fr_dlg(HWND hWnd, BOOL replDlg)
  {
  // fr_dlg ist nur eine Huelse und kehrt sofort zum Aufrufer zurueck; werden die Buttons betaetigt,
  // werden fr_msg-Nachrichten erzeugt, die die eigentlichen Arbeitsfunktionen fr_find*() ansteuern.
  // Nachfolgende Aenderungen von Suchoptionen (matchCase etc.) in fr_dlgStruc werden ueber Dialog
  // sofort an Variable fr_dlgStruc gegeben, sodass Arbeitsfunktionen immer aktuellen Zustand haben.
  fr_findCt = fr_replCt = 0;
  #ifdef BUILD_FR_PR
  fr_preset(GetDlgItem(hWnd, IDW_WIN_EDIT)); // fr_FindText mit evt. Markierung vorbesetzen
  #endif
  ZeroMemory(&fr_dlgStruc, sizeof(FINDREPLACE));
  fr_dlgStruc.lStructSize = sizeof(FINDREPLACE);
  fr_dlgStruc.hwndOwner = hWnd; // Bestimmt, an welches Fenster Nachrichten gesendet werden
  fr_dlgStruc.Flags = FR_DOWN | FR_NOUPDOWN; // DOWN voreinstellen und fixieren
  fr_dlgStruc.lpstrFindWhat = fr_findStr;
  fr_dlgStruc.wFindWhatLen = MAX_STRING_SIZE;
  if (replDlg) {
    fr_dlgStruc.lpstrReplaceWith = fr_replStr; fr_dlgStruc.wReplaceWithLen  = MAX_STRING_SIZE;
    return ReplaceText(&fr_dlgStruc);
    }
  else
    return FindText(&fr_dlgStruc); // Dialog aufgerufen; Find/ReplaceText warten Benutzereingaben
    // in Suchfelder NICHT ab, sondern kehren sofort mit Rueckgabe von hFindDlg zurueck (moduslos).
  }

// fr_find() wird durch den "Suchen"-Knopf im Suchdialog oder im Suchen-Ersetzen-Dialog
// aufgerufen, fr_replace() durch den "Ersetzen"-Knopf im Suchen-Ersetzen-Dialog.
// Beide Routinen bilden ein Gespann:
// fr_find() sucht und markiert Text, fr_replace() ersetzt ihn. fr_repl() muß jedoch intern
// (d.h., ohne, daß Benutzer "Suchen" drückt) manchmal erst fr_find() aufrufen, z.B. beim
// ersten Aufruf des Dialogs, wenn noch kein zu ersetzender Text markiert ist.

/** Function: Suche Suchmuster **/
BOOL fr_find(HWND hWinEdit)
  {
  int sos, eos, start;
  TCHAR *buf, *found;
  // Dokument in Puffer kopieren und dort suchen, da es leider kein direktes Find fuer EDIT-Controls
  // gibt, wohl aber fuer RICHEDIT! Puffer muss auch jedesmal neu angelegt werden, da bei einem
  // moduslosem Dialog der Benutzer Text zwischenzeitlich aendern koennte.
  fr_focusLost = 0;
  if ((buf=ut_getEditText(hWinEdit,NULL)) == NULL)
    return FALSE;
  /* Suche Text im Dokument */
  SendMessage(hWinEdit, EM_GETSEL, (WPARAM)&sos, (LPARAM)&eos);
  start = (sos != eos) ? sos+1 : sos;
  found = ut_strstr(buf+start, fr_dlgStruc.lpstrFindWhat, fr_dlgStruc.Flags&FR_MATCHCASE,
    fr_dlgStruc.Flags&FR_WHOLEWORD);
  if (found)
    start = (int)(found - buf); // FIX 1.4: Typecast fuer WIN64
  GlobalFree(buf);
  if (found == NULL)
    return FALSE;
  /* Markiere gefundenen Text fuer Benutzer */
  SendMessage(hWinEdit, EM_SETSEL, start, start+lstrlen(fr_dlgStruc.lpstrFindWhat));
  SendMessage(hWinEdit, EM_SCROLLCARET, 0, 0); // Aktualisiere Fenster, so dass Caret sichtbar wird
  ++fr_findCt;
  return TRUE;
  }

/** Function: Ersetze naechstes Suchmuster **/
static BOOL fr_repl(HWND hWinEdit)
  {
  int sos, eos, selIsValid;
  TCHAR *sel;
  SendMessage(hWinEdit, EM_GETSEL, (WPARAM)&sos, (LPARAM)&eos);

  /* A - FIND muss erst laufen ... */
  if (sos == eos) // weil kein Text zur Ersetzung markiert ist oder
    return fr_find(hWinEdit);
  if (fr_focusLost) { // weil der Benutzer zwischenzeitlich im EDIT-Control war
    selIsValid = 0; //  und die Markierung nicht mehr stimmen muss
    if ((sel=ut_getEditSel(hWinEdit, NULL)) != NULL) {
      selIsValid = lstrcmp(sel,fr_dlgStruc.lpstrFindWhat) == 0;
      GlobalFree(sel); // Markierung stimmt nicht!
      }
    if (!selIsValid) {
      SendMessage(hWinEdit, EM_SETSEL, sos, sos);
      return fr_find(hWinEdit);
      }
    }

  /* B - REPL */
  SendMessage(hWinEdit, EM_REPLACESEL, TRUE, (LPARAM)fr_dlgStruc.lpstrReplaceWith);
  ++fr_replCt;

  /* C - FIND markiert (sichtbar für Benutzer) den nächsten zu ersetzenden String */
  return fr_find(hWinEdit);
  }

/** Function: Ersetze alle Suchmuster **/
static UINT fr_replAll(HWND hWinEdit)
  {
  fr_findCt = fr_replCt = 0;
  SendMessage(hWinEdit, EM_SETSEL, 0, 0);
  while (fr_repl(hWinEdit))
    ;
  return fr_replCt;
  }

/** Function: Informiere andere Module ueber Suchmuster **/
TCHAR *fr_getFindStr(void)
  {
  return fr_findStr;
  }

/** Function: Dispatcher **/
int fr_dispatch(HWND hWinEdit)
  {
  // Returns:
  // -3 FR_DIALOGTERM
  // -2 No more found
  // -1 Something found
  // 0..n Count of replacements
  if (fr_dlgStruc.Flags & FR_DIALOGTERM)
    return -3;
  else
    if (fr_dlgStruc.Flags & FR_FINDNEXT)
      return fr_find(hWinEdit) ? -1 : -2;
    else
      if (fr_dlgStruc.Flags & FR_REPLACE)
        return fr_repl(hWinEdit) ? -1 : -2;
      else
        if (fr_dlgStruc.Flags & FR_REPLACEALL)
          return fr_replAll(hWinEdit);
        else
          return -4; // Unknown
  }

/** Function: Informiere FR-Modul, dass Benutzer in das Hauptfenster gewechselt war **/
void fr_setFocusLost(void)
  {
  fr_focusLost = 1; // fr_repl() fragt spaeter fr_setFocusLost ab und fr_find setzt es zurueck
  }

#endif

#endif