6

Can anybody give me some simple code that would give me the ability to search a simple string in a memo and have it highlighted in the memo after being found?

Grzegorz Oledzki
  • 23,614
  • 16
  • 68
  • 106
micheal
  • 119
  • 5
  • 7

2 Answers2

11

This search allows for document wrap, case (in)sensitive search and searching from cursor position.

type
  TSearchOption = (soIgnoreCase, soFromStart, soWrap);
  TSearchOptions = set of TSearchOption;


function SearchText(
    Control: TCustomEdit; 
    Search: string; 
    SearchOptions: TSearchOptions): Boolean;
var
  Text: string;
  Index: Integer;
begin
  if soIgnoreCase in SearchOptions then
  begin
    Search := UpperCase(Search);
    Text := UpperCase(Control.Text);
  end
  else
    Text := Control.Text;

  Index := 0;
  if not (soFromStart in SearchOptions) then
    Index := PosEx(Search, Text, 
         Control.SelStart + Control.SelLength + 1);

  if (Index = 0) and 
      ((soFromStart in SearchOptions) or 
       (soWrap in SearchOptions)) then
    Index := PosEx(Search, Text, 1);

  Result := Index > 0;
  if Result then
  begin
    Control.SelStart := Index - 1;
    Control.SelLength := Length(Search);
  end;
end;

You can set HideSelection = False on the memo to show the selection even if the memo isn't focussed.

Use like this:

  SearchText(Memo1, Edit1.Text, []);

Allows searching edits as well.

GolezTrol
  • 114,394
  • 18
  • 182
  • 210
  • Using UpperCase might not give you the desired results, e.g. in French, the upper case characters cannot not have an accent while lower case characters can have one (that's different from Canadian French I think, where upper case characters can also be accented). So, in this case using LowerCase would give better results. – dummzeuch Nov 20 '10 at 14:40
  • Well, the accented letter and the unaccented letter are two different letters, aren't they? A french word, when converted to UpperCase will show like PRIVé, in lowercase it would be privé. On the other hand, the uppercase letter É isn't converted to é either, so I don't see how that will affect the search results. Though I must admit I'm testing this in Delphi 7. If unicode or the possible use of locale settings in your Delphi yields in better results when using LowerCase, please do. – GolezTrol Nov 20 '10 at 23:21
  • 2
    GolezTrol: Thanks for the HideSelection tip! – RobertFrank Nov 21 '10 at 21:30
3
  function TForm1.FindText( const aPatternToFind: String):Boolean;
  var
    p: Integer;
  begin
    p := pos(aPatternToFind, Memo1.Text);
    Result :=  (p > 0);
    if Result then
      begin
        Memo1.SelStart := p;
        Memo1.SelLength := Length(aPatternToFind);
        Memo1.SetFocus; // necessary so highlight is visible
      end;
  end;

This does NOT search across lines if WordWrap is true.

RobertFrank
  • 7,332
  • 11
  • 53
  • 99
  • Searching in `TStrings.Text` rather than `TStrings.Strings` is very expensive. 2×N in the fact. – Free Consulting Nov 23 '10 at 10:49
  • @user205376 - Why do you say that? TStrings.Text is a single string on which I'd think pos() would be fast. Searching, as you suggest, TStrings.Strings would involve subscripts to access each string. That, IMO, opinion, would be slower and provide no additional functionality that the pos() doesn't. (And, it would require some tricky code to highlight the found pattern) Why do you think not? – RobertFrank Nov 25 '10 at 18:27
  • A bit late to the game, but for the above, it depends on the implementation. After all TStrings is abstract. Memo.Lines.Text (= TMemoStrings.Text) gets the text of the memo using a single API call, while a TStringList stores separate string and TStringList.Text combines them into a single string on each request (which is often still faster than calling the API, btw). The answer uses Memo1.Text which I think bypasses any TStrings descendant and directly calls some GetText api on the memo's handle. – GolezTrol Aug 21 '17 at 07:40