| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363 |
- unit IDE.MainForm.FindReplaceHelper;
- {
- Inno Setup
- Copyright (C) 1997-2025 Jordan Russell
- Portions by Martijn Laan
- For conditions of distribution and use, see LICENSE.TXT.
- Compiler form - Find & Replace helper which has the navigation helper as ancestor
- Not used by MainForm: it uses IDE.MainForm.FinalHelper instead
- }
- interface
- uses
- Dialogs,
- IDE.MainForm, IDE.MainForm.NavigationHelper;
- type
- TMainFormFindReplaceHelper = class helper(TMainFormNavigationHelper) for TMainForm
- procedure ShowFindDialog(const Down: Boolean);
- procedure ShowFindInFilesDialog;
- procedure DoFindNext(const Down: Boolean);
- procedure DoFindOrReplaceDialogFind(const Dialog: TFindDialog);
- procedure DoFindInFilesDialogFind;
- procedure UpdateFindResult(const FindResult: TFindResult; const ItemIndex: Integer;
- const NewLine, NewLineStartPos: Integer);
- function FindSetupDirectiveValue(const DirectiveName,
- DefaultValue: String): String; overload;
- function FindSetupDirectiveValue(const DirectiveName: String;
- DefaultValue: Boolean): Boolean; overload;
- procedure ShowReplaceDialog;
- procedure DoReplaceDialogReplace;
- { Private }
- procedure _InitializeFindText(Dlg: TFindDialog);
- procedure _FindNext(const ReverseDirection: Boolean);
- function _StoreAndTestLastFindOptions(const Dialog: TFindDialog): Boolean;
- function _TestLastFindOptions: Boolean;
- end;
- implementation
- uses
- Windows, Messages,
- Classes, SysUtils, StrUtils, Menus,
- ScintEdit,
- Shared.CommonFunc, Shared.CommonFunc.Vcl,
- IDE.Messages, IDE.HelperFunc, IDE.ScintStylerInnoSetup;
- const
- OldFindReplaceWndProcProp = 'OldFindReplaceWndProc';
- function FindReplaceWndProc(Wnd: HWND; Msg: Cardinal; WParam: WPARAM; LParam: LPARAM): LRESULT; stdcall;
- function CallDefWndProc: LRESULT;
- begin
- Result := CallWindowProc(Pointer(GetProp(Wnd, OldFindReplaceWndProcProp)), Wnd,
- Msg, WParam, LParam);
- end;
- begin
- case Msg of
- WM_MENUCHAR:
- if LoWord(wParam) = VK_RETURN then begin
- var hwndCtl := GetDlgItem(Wnd, idOk);
- if (hWndCtl <> 0) and IsWindowEnabled(hWndCtl) then
- PostMessage(Wnd, WM_COMMAND, MakeWParam(idOk, BN_CLICKED), Windows.LPARAM(hWndCtl));
- end;
- WM_NCDESTROY:
- begin
- Result := CallDefWndProc;
- RemoveProp(Wnd, OldFindReplaceWndProcProp);
- Exit;
- end;
- end;
- Result := CallDefWndProc;
- end;
- procedure ExecuteFindDialogAllowingAltEnter(const FindDialog: TFindDialog);
- begin
- var DoHook := FindDialog.Handle = 0;
- FindDialog.Execute;
- if DoHook then begin
- SetProp(FindDialog.Handle, OldFindReplaceWndProcProp, GetWindowLong(FindDialog.Handle, GWL_WNDPROC));
- SetWindowLongPtr(FindDialog.Handle, GWL_WNDPROC, LONG_PTR(@FindReplaceWndProc));
- end;
- end;
- { TMainFormFindReplaceHelper }
- procedure TMainFormFindReplaceHelper._InitializeFindText(Dlg: TFindDialog);
- var
- S: String;
- begin
- S := FActiveMemo.MainSelText;
- if (S <> '') and (Pos(#13, S) = 0) and (Pos(#10, S) = 0) then
- Dlg.FindText := S
- else
- Dlg.FindText := FLastFindText;
- end;
- procedure TMainFormFindReplaceHelper.ShowFindDialog(const Down: Boolean);
- begin
- ReplaceDialog.CloseDialog;
- if FindDialog.Handle = 0 then
- _InitializeFindText(FindDialog);
- if Down then
- FindDialog.Options := FindDialog.Options + [frDown]
- else
- FindDialog.Options := FindDialog.Options - [frDown];
- ExecuteFindDialogAllowingAltEnter(FindDialog);
- end;
- procedure TMainFormFindReplaceHelper.ShowFindInFilesDialog;
- begin
- _InitializeFindText(FindInFilesDialog);
- FindInFilesDialog.Execute;
- end;
- procedure TMainFormFindReplaceHelper.DoFindNext(const Down: Boolean);
- begin
- if FLastFindText = '' then
- ShowFindDialog(Down)
- else begin
- if Down then
- FLastFindOptions := FLastFindOptions + [frDown]
- else
- FLastFindOptions := FLastFindOptions - [frDown];
- FLastFindRegEx := FOptions.FindRegEx;
- if not _TestLastFindOptions then
- Exit;
- _FindNext(False);
- end;
- end;
- procedure TMainFormFindReplaceHelper._FindNext(const ReverseDirection: Boolean);
- var
- StartPos, EndPos: Integer;
- Range: TScintRange;
- begin
- var Down := frDown in FLastFindOptions;
- if ReverseDirection then
- Down := not Down;
- if Down then begin
- StartPos := FActiveMemo.Selection.EndPos;
- EndPos := FActiveMemo.RawTextLength;
- end
- else begin
- StartPos := FActiveMemo.Selection.StartPos;
- EndPos := 0;
- end;
- if FActiveMemo.FindText(StartPos, EndPos, FLastFindText,
- FindOptionsToSearchOptions(FLastFindOptions, FLastFindRegEx), Range) then
- FActiveMemo.SelectAndEnsureVisible(Range)
- else
- MsgBoxFmt('Cannot find "%s"', [FLastFindText], SCompilerFormCaption,
- mbInformation, MB_OK);
- end;
- function TMainFormFindReplaceHelper._StoreAndTestLastFindOptions(const Dialog: TFindDialog): Boolean;
- begin
- { TReplaceDialog is a subclass of TFindDialog must check for TReplaceDialog first }
- if Dialog is TReplaceDialog then begin
- with Dialog as TReplaceDialog do begin
- FLastFindOptions := Options;
- FLastFindText := FindText;
- end;
- end else begin
- with Dialog do begin
- FLastFindOptions := Options;
- FLastFindText := FindText;
- end;
- end;
- FLastFindRegEx := FOptions.FindRegEx;
- Result := _TestLastFindOptions;
- end;
- function TMainFormFindReplaceHelper._TestLastFindOptions;
- begin
- if FLastFindRegEx then begin
- Result := FActiveMemo.TestRegularExpression(FLastFindText);
- if not Result then
- MsgBoxFmt('Invalid regular expression "%s"', [FLastFindText], SCompilerFormCaption,
- mbError, MB_OK);
- end else
- Result := True;
- end;
- procedure TMainFormFindReplaceHelper.DoFindOrReplaceDialogFind(const Dialog: TFindDialog);
- begin
- if not _StoreAndTestLastFindOptions(Dialog) then
- Exit;
- if GetKeyState(VK_MENU) < 0 then begin
- { Alt+Enter was used to close the dialog }
- Dialog.CloseDialog;
- ESelectAllFindMatchesClick(Self); { Uses the copy made above }
- end else
- _FindNext(GetKeyState(VK_SHIFT) < 0);
- end;
- procedure TMainFormFindReplaceHelper.DoFindInFilesDialogFind;
- begin
- if not _StoreAndTestLastFindOptions(FindInFilesDialog) then
- Exit;
- FindResultsList.Clear;
- SendMessage(FindResultsList.Handle, LB_SETHORIZONTALEXTENT, 0, 0);
- FFindResults.Clear;
- var Hits := 0;
- var Files := 0;
- for var Memo in FFileMemos do begin
- if Memo.Used then begin
- var StartPos := 0;
- var EndPos := Memo.RawTextLength;
- var FileHits := 0;
- var Range: TScintRange;
- while (StartPos < EndPos) and
- Memo.FindText(StartPos, EndPos, FLastFindText,
- FindOptionsToSearchOptions(FLastFindOptions, FLastFindRegEx), Range) do begin
- { Also see UpdateFindResult }
- var Line := Memo.GetLineFromPosition(Range.StartPos);
- var Prefix := Format(' Line %d: ', [Line+1]);
- var FindResult := TFindResult.Create;
- FindResult.Filename := Memo.Filename;
- FindResult.Line := Line;
- FindResult.LineStartPos := Memo.GetPositionFromLine(Line);
- FindResult.Range := Range;
- FindResult.PrefixStringLength := Length(Prefix);
- FFindResults.Add(FindResult);
- FindResultsList.Items.AddObject(Prefix + Memo.Lines[Line], FindResult);
- Inc(FileHits);
- StartPos := Range.EndPos;
- end;
- Inc(Files);
- if FileHits > 0 then begin
- Inc(Hits, FileHits);
- FindResultsList.Items.Insert(FindResultsList.Count-FileHits, Format('%s (%d hits):', [Memo.Filename, FileHits]));
- end;
- end;
- end;
- FindResultsList.Items.Insert(0, Format('Find "%s" (%d hits in %d files)', [FindInFilesDialog.FindText, Hits, Files]));
- FindInFilesDialog.CloseDialog;
- OutputTabSet.TabIndex := tiFindResults;
- SetStatusPanelVisible(True);
- end;
- procedure TMainFormFindReplaceHelper.UpdateFindResult(const FindResult: TFindResult; const ItemIndex: Integer;
- const NewLine, NewLineStartPos: Integer);
- begin
- { Also see DoFindInFilesDialogFind }
- const OldPrefix = Format(' Line %d: ', [FindResult.Line+1]);
- FindResult.Line := NewLine;
- const NewPrefix = Format(' Line %d: ', [FindResult.Line+1]);
- FindResultsList.Items[ItemIndex] := NewPrefix + Copy(FindResultsList.Items[ItemIndex], Length(OldPrefix)+1, MaxInt);
- FindResult.PrefixStringLength := Length(NewPrefix);
- const PosChange = NewLineStartPos - FindResult.LineStartPos;
- FindResult.LineStartPos := NewLineStartPos;
- FindResult.Range.StartPos := FindResult.Range.StartPos + PosChange;
- FindResult.Range.EndPos := FindResult.Range.EndPos + PosChange;
- end;
- function TMainFormFindReplaceHelper.FindSetupDirectiveValue(const DirectiveName,
- DefaultValue: String): String;
- begin
- Result := DefaultValue;
- var Memo := FMainMemo; { This function only searches the main file }
- var StartPos := 0;
- var EndPos := Memo.RawTextLength;
- var Range: TScintRange;
- { We rely on the styler to identify [Setup] section lines, but we
- may be searching into areas that haven't been styled yet }
- Memo.StyleNeeded(EndPos);
- while (StartPos < EndPos) and
- Memo.FindText(StartPos, EndPos, DirectiveName, [sfoWholeWord], Range) do begin
- var Line := Memo.GetLineFromPosition(Range.StartPos);
- if FMemosStyler.GetSectionFromLineState(Memo.Lines.State[Line]) = scSetup then begin
- var LineValue := Memo.Lines[Line].Trim; { LineValue can't be empty }
- if LineValue[1] <> ';' then begin
- var LineParts := LineValue.Split(['=']);
- if (Length(LineParts) = 2) and SameText(LineParts[0].Trim, DirectiveName) then begin
- Result := LineParts[1].Trim;
- { If Result is surrounded in quotes, remove them, just like TSetupCompiler.SeparateDirective }
- if (Length(Result) >= 2) and
- (Result[1] = '"') and (Result[Length(Result)] = '"') then
- Result := Copy(Result, 2, Length(Result)-2);
- Exit; { Compiler doesn't allow a directive to be specified twice so we can exit now }
- end;
- end;
- end;
- StartPos := Range.EndPos;
- end;
- end;
- function TMainFormFindReplaceHelper.FindSetupDirectiveValue(const DirectiveName: String;
- DefaultValue: Boolean): Boolean;
- begin
- var Value := FindSetupDirectiveValue(DirectiveName, IfThen(DefaultValue, '1', '0'));
- if not TryStrToBoolean(Value, Result) then
- Result := DefaultValue;
- end;
- procedure TMainFormFindReplaceHelper.ShowReplaceDialog;
- begin
- FindDialog.CloseDialog;
- if ReplaceDialog.Handle = 0 then begin
- _InitializeFindText(ReplaceDialog);
- ReplaceDialog.ReplaceText := FLastReplaceText;
- end;
- ExecuteFindDialogAllowingAltEnter(ReplaceDialog);
- end;
- procedure TMainFormFindReplaceHelper.DoReplaceDialogReplace;
- begin
- if not _StoreAndTestLastFindOptions(ReplaceDialog) then
- Exit;
- FLastReplaceText := ReplaceDialog.ReplaceText;
- var ReplaceMode := RegExToReplaceMode(FLastFindRegEx);
- if frReplaceAll in FLastFindOptions then begin
- var ReplaceCount := 0;
- FActiveMemo.BeginUndoAction;
- try
- var Pos := 0;
- var Range: TScintRange;
- while FActiveMemo.FindText(Pos, FActiveMemo.RawTextLength, FLastFindText,
- FindOptionsToSearchOptions(FLastFindOptions, FLastFindRegEx), Range) do begin
- var NewRange := FActiveMemo.ReplaceTextRange(Range.StartPos, Range.EndPos, FLastReplaceText, ReplaceMode);
- Pos := NewRange.EndPos;
- Inc(ReplaceCount);
- end;
- finally
- FActiveMemo.EndUndoAction;
- end;
- if ReplaceCount = 0 then
- MsgBoxFmt('Cannot find "%s"', [FLastFindText], SCompilerFormCaption,
- mbInformation, MB_OK)
- else
- MsgBoxFmt('%d occurrence(s) replaced.', [ReplaceCount], SCompilerFormCaption,
- mbInformation, MB_OK);
- end
- else begin
- if FActiveMemo.MainSelTextEquals(FLastFindText, FindOptionsToSearchOptions(frMatchCase in FLastFindOptions, FLastFindRegEx)) then begin
- { Note: the MainSelTextEquals above performs a search so the replacement
- below is safe even if the user just enabled regex }
- FActiveMemo.ReplaceMainSelText(FLastReplaceText, ReplaceMode);
- end;
- _FindNext(GetKeyState(VK_SHIFT) < 0);
- end;
- end;
- end.
|