Text suchen


Im letzten Kapitel wurde es bereits erwähnt: wenn wir den Suchendialog ausführen (in dem Sinn, dass wir Text eingeben und auf den Button "Weitersuchen" klicken), dann wird an unser Fenster die Nachricht FINDMSGSTRING gesendet. Dies ist keine Konstante, sondern wir mussten den Wert dieser Nachricht zuerst ermitteln.
Wir sollten daher vor der Bearbeitung der Nachricht prüfen, ob überhaupt ein Wert zugewiesen wurde. Der lParam-Wert ist dann ein Zeiger auf ein TFindReplace-Record:

case uMsg of
  { ... }
// Suchfunktion, by NicoDE
else if(FindTextMsgId <> 0) and (uMsg = FindTextMsgId) then begin FindParam := PFindReplace(lp);

Die Flags-Membervariable dieses Records enthält nun den Wert FR_FINDNEXT, wenn wir im Suchendialog den Button "Weitersuchen" anklicken. In diesem Fall rufen wir die eigentliche Suchfunktion, "SearchText" auf, der wir neben Besitzerfenster und Suchtext auch die Flags übergeben, mit denen wir im Suchendialog festlegen können, in welcher Richtung wir suchen wollen, ob ganze Wörter gesucht werden sollen und ob die Groß- und Kleinschreibung eine Rolle spielt:

    if(FindParam.Flags and FR_FINDNEXT = FR_FINDNEXT) then
      SearchText(FindParam.hWndOwner,FindParam.lpstrFindWhat,
        FindParam.Flags and FR_DOWN = FR_DOWN,
        FindParam.Flags and FR_MATCHCASE = FR_MATCHCASE,
        FindParam.Flags and FR_WHOLEWORD = FR_WHOLEWORD);
  end


Da wir stets mit dem Besitzerfenster arbeiten, unserem Editorfenster also, müssen wir in der Suchfunktion zuerst das Handle des RichEdit-Controls ermitteln. Dazu dient uns die von den Dialogen bekannte Funktion "GetDlgItem":

rEdit := GetDlgItem(wnd,IDC_EDIT);

Dann stellen wir fest, ob in dem Control bereits Text markiert ist. Dabei hilft uns die Nachricht "EM_EXGETSEL", die als lParam einen Zeiger auf ein TCharRange-Record erwartet. Da wir den Text noch suchen wollen, benutzen wir hier gleich die entsprechende Membervariable eines TFindText -Records:

SendMessage(rEdit,EM_EXGETSEL,0,LPARAM(@FindRec.chrg));

Die cpMax-Membervariable setzen wir vor jedem Aufruf auf -1, bei der cpMin-Variable kommt es auf unsere Suchrichtung an. Suchen wir in Richtung Textende (Abwärts), dann genügt es, den alten Wert von cpMax einstellen. Suchen wir in Richtung Textanfang (Aufwärts), dann sollten wir den alten Wert von cpMin beibehalten.

if(Down) then FindRec.chrg.cpMin := FindRec.chrg.cpMax;
FindRec.chrg.cpMax := -1;

Warum?
Stellen wir uns dazu einfach einen kleinen Beispieltext vor, den wir nach irgendeinem Wort durchsuchen; sagen wir: Tutorial. Wir werden gleich noch sehen, wie der gefundene Text markiert wird. Und genau das ist der Punkt! Die o.g. Nachricht liefert nun die Markierung zurück, wobei cpMin natürlich den Beginn und cpMax das Ende des Wortes darstellen. Im Fall von Tutorial also die Positionen des "T" und "l".
Wollen wir abwärts (in Richtung Textende) suchen, müssen wir als neue Startposition nur den alten Endpunkt einstellen. Auf die Weise geht die Suche beim nächsten Mal hinter dem bereits gefundenen Wort weiter.
Anders sieht es dagegen mit der Suche in Richtung Textanfang aus. Würden wir auch hier den alten Endwert als neuen Startwert einstellen, würde die Suche scheinbar nicht funktionieren. Tatsächlich findet sie jedoch immer das selbe Wort, da wir ja ständig beim Ende des Wortes neu suchen lassen. Behalten wir den Wert von cpMin allerdings bei, liefert die Suche auch in der Aufwärtsrichtung mehr als nur ein Ergebnis (sofern der Suchbegriff entsprechend mehrfach vorkommt, natürlich!).

Um Text zu finden, müssen wir diesen natürlich auch angeben; in dem Fall weisen wir ihn dem TFindText-Record zu.

FindRec.lpstrText    := Text;

Von Interesse sind natürlich noch die Flags, die immerhin Auswirkungen auf die Suchergebnisse haben. In welche Richtung suchen wir? Werden nur ganze Wörter gewünscht? Spielt die Schreibweise eine Rolle? All diese Fragen können wir mit Hilfe der Variablen klären, die der Prozedur übergeben wurden:

Flags                := 0;
if(Down) then Flags  := FR_DOWN;
if(Sense) then Flags := Flags or FR_MATCHCASE;
if(Whole) then Flags := Flags or FR_WHOLEWORD;


Nun, dann suchen wir mal. Das RichEdit bietet zu dem Zweck die Nachricht "EM_FINDTEXT", die als wParam die eben gesetzten Flags und als lParam einen Zeiger auf das zuerst verwendete TFindText-Record erwartet. Rückgabewert ist die Position, an der der gesuchte Text gefunden wurde:

FindPos := SendMessage(rEdit,EM_FINDTEXT,Flags,LPARAM(@FindRec));

Ist der Wert größer Null, dann können wir den gefundenen Text mit Hilfe der Nachricht "EM_EXSETSEL" markieren:

if(FindPos > 0) then
begin
  FindRec.chrg.cpMin := FindPos;
  FindRec.chrg.cpMax := FindPos + lstrLen(Text);
  SendMessage(rEdit,EM_EXSETSEL,0,LPARAM(@FindRec.chrg));
end
else begin
  lstrcpy(ErrMsg,pchar('"' + Text + '"'));
  lstrcat(ErrMsg,' kann nicht gefunden werden.');
  MessageBox(wnd,ErrMsg,'editor',MB_ICONINFORMATION);
end;

Kann das gesuchte Wort nicht (mehr) gefunden werden, erscheint eine entsprechende Meldung.