Reputation: 63
We are working with XE7. We are trying to build a search and replace feature into a TRichEdit
.
We would like it to work just like it works in the Delphi compiler. We display our own search and replace dialog, we are not using the Delphi replace dialog.
We display the dialog, and when the user clicks the Replace button, the dialog closes, and for each instance found we want to popup a message dialog asking the user whether they want to replace that occurrence.
When we do this, the user cannot see what is selected, and if they click Yes on the message dialog, nothing is being replaced.
Our code is below:
procedure Tviewform.select_text(fndpos:integer);
begin
setfocus;
asciieditor.selstart:=fndpos;
asciieditor.sellength:=length(vwvars[curviewwin]^.finddata);
end;
procedure Tviewform.search_and_replace;
var position,endpos,fndans,cont:integer; foptions:tsearchtypes; msgques,stopall:word;
begin
with asciieditor do
begin
endpos:=length(asciieditor.text)-vwvars[curviewwin]^.sstartpos;
foptions:=[];
stopall:=0;
cont:=0;
if findinfo.onbut.checked then foptions:=foptions+[stMatchCase];
lines.beginupdate;
fndans:=findtext(vwvars[curviewwin]^.finddata,vwvars[curviewwin]^.sstartpos,endpos,foptions);
while (fndans<>-1) and (stopall=0) do
begin
select_text(fndans);
msgques:=mainform.silang1.messagedlg('Replace with '+vwvars[curviewwin]^.replace+'?.',mtinformation,[mbyes,mbno,mbcancel],0);
if msgques=mryes then
begin
asciieditor.clearselection;
seltext:=vwvars[curviewwin]^.replace;
end else
begin
if msgques=mrcancel then
begin
cont:=0;
stopall:=1;
end else cont:=1;
end;
asciieditor.Repaint;
if cont=1 then
begin
inc(vwvars[curviewwin]^.sstartpos,length(vwvars[curviewwin]^.finddata));
endpos:=length(asciieditor.text)-vwvars[curviewwin]^.sstartpos;
fndans:=findtext(vwvars[curviewwin]^.finddata,vwvars[curviewwin]^.sstartpos,endpos,foptions);
end;
end;
lines.endupdate;
end;
end;
Upvotes: 0
Views: 334
Reputation: 598279
I see a number of issues with your code:
You are moving input focus away from the TRichEdit
when you call select_text()
and MessageDlg()
, so make sure that TRichEdit.HideSelection
is set to False
(it is True
by default) in order to actually see the selection.
You are calling BeginUpdate()
on the TRichEdit
, which disables screen redraws for it, including Repaint()
.
You don't need to call TRichEdit.ClearSelection()
before setting the TRichEdit.SelText
.
When updating vwvars[curviewwin]^.sstartpos
after each replacement, you need to reset it to fndAns
(the current selection's position) plus the length of vwvars[curviewwin]^.replace
(the new text), not the length of vwvars[curviewwin]^.finddata
(the old text). You are not moving it beyond the new text, so you are searching older text over and over.
If the user chooses No in the MessageDlg()
, you are not updating vwvars[curviewwin]^.sstartpos
to move past the text that the user didn't want to replace.
When setting endpos
, length(asciieditor.text)
can be asciieditor.gettextlen()
instead for better efficiency. But more importantly, you should not be subtracting vwvars[curviewwin]^.sstartpos
from endpos
at all. By doing that, you are skipping more and more text at the end of the TRichEdit
from the search with each replacement made. In fact, you should be using just the asciieditor
's current text length as the end position of each search, so you don't actually need endpos
at all.
Depending on the size of your TRichEdit
and its content, if it has scrollbars enabled, you should send the TRichEdit
a EM_SCROLLCARET
message each time you find a matching text and before you prompt the user with the MessageDlg()
, in case the matching text is off-screen.
With that said, try something more like this:
procedure Tviewform.search_and_replace;
var
fndAns: Integer;
fOptions: TSearchTypes;
msgQues: Word;
procedure select_text;
begin
{AsciiEditor.}SetFocus;
AsciiEditor.SelStart := fndAns;
AsciiEditor.SelLength := Length(vwvars[curviewwin]^.finddata);
AsciiEditor.Perform(EM_SCROLLCARET, 0, 0);
AsciiEditor.Update;
end;
begin
fOptions := [];
if FindInfo.OnBut.Checked then Include(fOptions, stMatchCase);
repeat
fndAns := AsciiEditor.FindText(vwvars[curviewwin]^.finddata, vwvars[curviewwin]^.sstartpos, AsciiEditor.GetTextLen, fOptions);
if fndAns = -1 then Break;
select_text;
msgQues := MainForm.SiLang1.MessageDlg('Replace with ' + vwvars[curviewwin]^.replace + '?', mtinformation, [mbYes,mbNo,mbCancel], 0);
if msgQues = mrYes then
begin
AsciiEditor.SelText := vwvars[curviewwin]^.replace;
AsciiEditor.Update;
Inc(fndAns, Length(vwvars[curviewwin]^.replace));
end
else if msgQues = mrNo then
begin
Inc(fndAns, Length(vwvars[curviewwin]^.finddata));
end else
Break;
vwvars[curviewwin]^.sstartpos := fndAns;
until False;
end;
Upvotes: 1