mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-06 01:57:57 +02:00
506 lines
17 KiB
ObjectPascal
506 lines
17 KiB
ObjectPascal
{
|
|
*****************************************************************************
|
|
See the file COPYING.modifiedLGPL.txt, included in this distribution,
|
|
for details about the license.
|
|
*****************************************************************************
|
|
|
|
Author: Mattias Gaertner
|
|
|
|
Abstract:
|
|
Find in files dialog form.
|
|
|
|
}
|
|
unit FindInFilesDlg;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
Classes, SysUtils,
|
|
FileProcs, LazFileUtils,
|
|
LCLProc, Controls, StdCtrls, Forms, Buttons, ExtCtrls, Dialogs, ButtonPanel,
|
|
SynEditTypes, SynEdit,
|
|
MacroIntf, IDEWindowIntf, SrcEditorIntf, IDEHelpIntf, IDEDialogs,
|
|
LazarusIDEStrConsts, InputHistory, EditorOptions, Project, IDEProcs,
|
|
SearchFrm, SearchResultView;
|
|
|
|
type
|
|
{ TLazFindInFilesDialog }
|
|
|
|
TLazFindInFilesDialog = class(TForm)
|
|
ButtonPanel1: TButtonPanel;
|
|
ReplaceCheckBox: TCheckBox;
|
|
ReplaceTextComboBox: TComboBox;
|
|
IncludeSubDirsCheckBox: TCheckBox;
|
|
FileMaskComboBox: TComboBox;
|
|
DirectoriesBrowse: TBitBtn;
|
|
DirectoriesComboBox: TComboBox;
|
|
DirectoriesLabel: TLabel;
|
|
FileMaskLabel: TLabel;
|
|
DirectoriesOptionsGroupBox: TGroupBox;
|
|
OptionsCheckGroupBox: TCheckGroup;
|
|
SelectDirectoryDialog: TSelectDirectoryDialog;
|
|
TextToFindComboBox: TComboBox;
|
|
TextToFindLabel: TLabel;
|
|
WhereRadioGroup: TRadioGroup;
|
|
procedure DirectoriesBrowseClick(Sender: TObject);
|
|
procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
|
|
procedure FormCreate(Sender: TObject);
|
|
procedure HelpButtonClick(Sender: TObject);
|
|
procedure OKButtonClick(Sender : TObject);
|
|
procedure ReplaceCheckBoxChange(Sender: TObject);
|
|
procedure WhereRadioGroupClick(Sender: TObject);
|
|
private
|
|
FProject: TProject;
|
|
function GetFindText: string;
|
|
function GetOptions: TLazFindInFileSearchOptions;
|
|
function GetReplaceText: string;
|
|
function GetSynOptions: TSynSearchOptions;
|
|
procedure SetFindText(const NewFindText: string);
|
|
procedure SetOptions(NewOptions: TLazFindInFileSearchOptions);
|
|
procedure SetReplaceText(const AValue: string);
|
|
procedure SetSynOptions(NewOptions: TSynSearchOptions);
|
|
procedure UpdateReplaceCheck;
|
|
public
|
|
property Options: TLazFindInFileSearchOptions read GetOptions
|
|
write SetOptions;
|
|
property FindText: string read GetFindText write SetFindText;
|
|
property ReplaceText: string read GetReplaceText write SetReplaceText;
|
|
property SynSearchOptions: TSynSearchOptions read GetSynOptions
|
|
write SetSynOptions;
|
|
function GetBaseDirectory: string;
|
|
procedure LoadHistory;
|
|
procedure SaveHistory;
|
|
procedure FindInSearchPath(SearchPath: string);
|
|
procedure FindInFilesPerDialog(AProject: TProject);
|
|
procedure InitFindText;
|
|
procedure InitFromLazSearch(Sender: TObject);
|
|
procedure FindInFiles(AProject: TProject; const AFindText: string);
|
|
function GetResolvedDirectories: string;
|
|
function Execute: boolean;
|
|
property LazProject: TProject read FProject write FProject;
|
|
end;
|
|
|
|
function FindInFilesDialog: TLazFindInFilesDialog;
|
|
|
|
implementation
|
|
|
|
const // WhereRadioGroup's ItemIndex in a more informative form.
|
|
ItemIndProject = 0;
|
|
ItemIndOpenFiles = 1;
|
|
ItemIndDirectories = 2;
|
|
ItemIndActiveFile = 3;
|
|
|
|
var
|
|
FindInFilesDialogSingleton: TLazFindInFilesDialog = nil;
|
|
|
|
function FindInFilesDialog: TLazFindInFilesDialog;
|
|
begin
|
|
if FindInFilesDialogSingleton = nil then
|
|
FindInFilesDialogSingleton := TLazFindInFilesDialog.Create(Application);
|
|
Result := FindInFilesDialogSingleton;
|
|
end;
|
|
|
|
{$R *.lfm}
|
|
|
|
{ TLazFindInFilesDialog }
|
|
|
|
procedure TLazFindInFilesDialog.SetFindText(const NewFindText: string);
|
|
begin
|
|
TextToFindComboBox.Text := NewFindText;
|
|
TextToFindComboBox.SelectAll;
|
|
ActiveControl := TextToFindComboBox;
|
|
end;
|
|
|
|
function TLazFindInFilesDialog.GetFindText: string;
|
|
begin
|
|
Result := TextToFindComboBox.Text;
|
|
end;
|
|
|
|
function TLazFindInFilesDialog.GetReplaceText: string;
|
|
begin
|
|
Result:=ReplaceTextComboBox.Text;
|
|
end;
|
|
|
|
procedure TLazFindInFilesDialog.WhereRadioGroupClick(Sender: TObject);
|
|
begin
|
|
DirectoriesOptionsGroupBox.Enabled := (WhereRadioGroup.ItemIndex = ItemIndDirectories)
|
|
end;
|
|
|
|
procedure TLazFindInFilesDialog.DirectoriesBrowseClick(Sender: TObject);
|
|
var
|
|
Dir: String;
|
|
OldDirs: String;
|
|
p: Integer;
|
|
begin
|
|
InitIDEFileDialog(SelectDirectoryDialog);
|
|
// use the first directory as initialdir for the dialog
|
|
OldDirs:=GetResolvedDirectories;
|
|
p:=1;
|
|
repeat
|
|
Dir:=GetNextDirectoryInSearchPath(OldDirs,p);
|
|
if Dir='' then break;
|
|
if DirectoryExistsUTF8(Dir) then break;
|
|
until false;
|
|
if Dir<>'' then
|
|
SelectDirectoryDialog.InitialDir := Dir
|
|
else
|
|
SelectDirectoryDialog.InitialDir := GetBaseDirectory;
|
|
|
|
if SelectDirectoryDialog.Execute then
|
|
DirectoriesComboBox.Text := AppendPathDelim(TrimFilename(SelectDirectoryDialog.FileName));
|
|
StoreIDEFileDialog(SelectDirectoryDialog);
|
|
end;
|
|
|
|
procedure TLazFindInFilesDialog.FormClose(Sender: TObject;
|
|
var CloseAction: TCloseAction);
|
|
begin
|
|
IDEDialogLayoutList.SaveLayout(Self);
|
|
end;
|
|
|
|
procedure TLazFindInFilesDialog.FormCreate(Sender: TObject);
|
|
begin
|
|
Caption := srkmecFindInFiles;
|
|
|
|
TextToFindLabel.Caption := dlgTextToFind;
|
|
ReplaceCheckBox.Caption := dlgReplaceWith;
|
|
|
|
OptionsCheckGroupBox.Caption := lisOptions;
|
|
OptionsCheckGroupBox.Items[0] := dlgCaseSensitive;
|
|
OptionsCheckGroupBox.Items[1] := dlgWholeWordsOnly;
|
|
OptionsCheckGroupBox.Items[2] := dlgRegularExpressions;
|
|
OptionsCheckGroupBox.Items[3] := lisFindFileMultiLinePattern;
|
|
|
|
WhereRadioGroup.Caption:=lisFindFileWhere;
|
|
WhereRadioGroup.Items[ItemIndProject] := lisFindFilesearchAllFilesInProject;
|
|
WhereRadioGroup.Items[ItemIndOpenFiles] := lisFindFilesearchAllOpenFiles;
|
|
WhereRadioGroup.Items[ItemIndDirectories] := lisFindFilesearchInDirectories;
|
|
WhereRadioGroup.Items[ItemIndActiveFile] := lisFindFilesearchInActiveFile;
|
|
|
|
DirectoriesOptionsGroupBox.Caption := lisDirectories;
|
|
DirectoriesComboBox.Hint:=lisMultipleDirectoriesAreSeparatedWithSemicolons;
|
|
DirectoriesLabel.Caption := lisFindFileDirectories;
|
|
FileMaskLabel.Caption := lisFindFileFileMask;
|
|
|
|
IncludeSubDirsCheckBox.Caption := lisFindFileIncludeSubDirectories;
|
|
|
|
ButtonPanel1.HelpButton.Caption := lisMenuHelp;
|
|
ButtonPanel1.CancelButton.Caption := lisCancel;
|
|
|
|
ReplaceCheckBox.Enabled:=true;
|
|
|
|
UpdateReplaceCheck;
|
|
DirectoriesOptionsGroupBox.Enabled:=WhereRadioGroup.ItemIndex=ItemIndDirectories;
|
|
|
|
AutoSize:=IDEDialogLayoutList.Find(Self,false)=nil;
|
|
IDEDialogLayoutList.ApplyLayout(Self);
|
|
end;
|
|
|
|
procedure TLazFindInFilesDialog.HelpButtonClick(Sender: TObject);
|
|
begin
|
|
LazarusHelp.ShowHelpForIDEControl(Self);
|
|
end;
|
|
|
|
procedure TLazFindInFilesDialog.OKButtonClick(Sender : TObject);
|
|
var
|
|
Dir: String;
|
|
p: Integer;
|
|
begin
|
|
if (WhereRadioGroup.ItemIndex=ItemIndDirectories) then
|
|
begin
|
|
Dir:=GetResolvedDirectories;
|
|
p:=1;
|
|
Dir:=GetNextDirectoryInSearchPath(Dir,p);
|
|
if not DirectoryExistsUTF8(Dir) then
|
|
begin
|
|
IDEMessageDialog(lisEnvOptDlgDirectoryNotFound,
|
|
Format(dlgSeachDirectoryNotFound,[Dir]),
|
|
mtWarning, [mbOk]);
|
|
ModalResult:=mrNone;
|
|
end;
|
|
end
|
|
end;
|
|
|
|
procedure TLazFindInFilesDialog.ReplaceCheckBoxChange(Sender: TObject);
|
|
begin
|
|
UpdateReplaceCheck;
|
|
end;
|
|
|
|
procedure TLazFindInFilesDialog.SetOptions(NewOptions: TLazFindInFileSearchOptions);
|
|
begin
|
|
OptionsCheckGroupBox.Checked[0] := fifMatchCase in NewOptions;
|
|
OptionsCheckGroupBox.Checked[1] := fifWholeWord in NewOptions;
|
|
OptionsCheckGroupBox.Checked[2] := fifRegExpr in NewOptions;
|
|
OptionsCheckGroupBox.Checked[3] := fifMultiLine in NewOptions;
|
|
DirectoriesOptionsGroupBox.Enabled := fifSearchDirectories in NewOptions;
|
|
IncludeSubDirsCheckBox.Checked := fifIncludeSubDirs in NewOptions;
|
|
ReplaceCheckBox.Checked := [fifReplace,fifReplaceAll]*NewOptions<>[];
|
|
|
|
if fifSearchProject in NewOptions then WhereRadioGroup.ItemIndex := ItemIndProject;
|
|
if fifSearchOpen in NewOptions then WhereRadioGroup.ItemIndex := ItemIndOpenFiles;
|
|
if fifSearchDirectories in NewOptions then WhereRadioGroup.ItemIndex := ItemIndDirectories;
|
|
if fifSearchActive in NewOptions then WhereRadioGroup.ItemIndex := ItemIndActiveFile;
|
|
|
|
UpdateReplaceCheck;
|
|
end;
|
|
|
|
function TLazFindInFilesDialog.GetOptions: TLazFindInFileSearchOptions;
|
|
begin
|
|
Result := [];
|
|
if OptionsCheckGroupBox.Checked[0] then Include(Result, fifMatchCase);
|
|
if OptionsCheckGroupBox.Checked[1] then Include(Result, fifWholeWord);
|
|
if OptionsCheckGroupBox.Checked[2] then Include(Result, fifRegExpr);
|
|
if OptionsCheckGroupBox.Checked[3] then Include(Result, fifMultiLine);
|
|
if IncludeSubDirsCheckBox.Checked then Include(Result, fifIncludeSubDirs);
|
|
if ReplaceCheckBox.Checked then Include(Result, fifReplace);
|
|
|
|
case WhereRadioGroup.ItemIndex of
|
|
ItemIndProject : Include(Result, fifSearchProject);
|
|
ItemIndOpenFiles : Include(Result, fifSearchOpen);
|
|
ItemIndDirectories: Include(Result, fifSearchDirectories);
|
|
ItemIndActiveFile : Include(Result, fifSearchActive);
|
|
end;//case
|
|
end;
|
|
|
|
function TLazFindInFilesDialog.GetSynOptions: TSynSearchOptions;
|
|
begin
|
|
Result := [];
|
|
if OptionsCheckGroupBox.Checked[0] then Include(Result, ssoMatchCase);
|
|
if OptionsCheckGroupBox.Checked[1] then Include(Result, ssoWholeWord);
|
|
if OptionsCheckGroupBox.Checked[2] then Include(Result, ssoRegExpr);
|
|
if OptionsCheckGroupBox.Checked[3] then Include(Result, ssoRegExprMultiLine);
|
|
if ReplaceCheckBox.Checked then Include(Result, ssoReplace);
|
|
end;//GetSynOptions
|
|
|
|
procedure TLazFindInFilesDialog.SetReplaceText(const AValue: string);
|
|
begin
|
|
ReplaceTextComboBox.Text := AValue;
|
|
end;
|
|
|
|
procedure TLazFindInFilesDialog.SetSynOptions(NewOptions: TSynSearchOptions);
|
|
begin
|
|
OptionsCheckGroupBox.Checked[0] := ssoMatchCase in NewOptions;
|
|
OptionsCheckGroupBox.Checked[1] := ssoWholeWord in NewOptions;
|
|
OptionsCheckGroupBox.Checked[2] := ssoRegExpr in NewOptions;
|
|
OptionsCheckGroupBox.Checked[3] := ssoRegExprMultiLine in NewOptions;
|
|
ReplaceCheckBox.Checked := ([ssoReplace,ssoReplaceAll]*NewOptions <> []);
|
|
|
|
UpdateReplaceCheck;
|
|
end;//SetSynOptions
|
|
|
|
procedure TLazFindInFilesDialog.UpdateReplaceCheck;
|
|
begin
|
|
ReplaceTextComboBox.Enabled:=ReplaceCheckBox.Checked;
|
|
if ReplaceCheckBox.Checked then
|
|
ButtonPanel1.OKButton.Caption := lisBtnReplace
|
|
else
|
|
ButtonPanel1.OKButton.Caption := lisBtnFind;
|
|
end;
|
|
|
|
function TLazFindInFilesDialog.GetBaseDirectory: string;
|
|
begin
|
|
Result:='';
|
|
if Project1<>nil then
|
|
Result:=Project1.ProjectDirectory;
|
|
if Result='' then
|
|
Result:=GetCurrentDirUTF8;
|
|
end;
|
|
|
|
const
|
|
SharedOptions = [ssoMatchCase,ssoWholeWord,ssoRegExpr,ssoRegExprMultiLine];
|
|
|
|
procedure TLazFindInFilesDialog.LoadHistory;
|
|
|
|
procedure AssignToComboBox(AComboBox: TComboBox; Strings: TStrings);
|
|
begin
|
|
AComboBox.Items.Assign(Strings);
|
|
if AComboBox.Items.Count>0 then
|
|
AComboBox.ItemIndex := 0;
|
|
end;
|
|
|
|
procedure AddFileToComboBox(AComboBox: TComboBox; Filename: string);
|
|
var
|
|
i: Integer;
|
|
begin
|
|
if Filename='' then exit;
|
|
Filename:=AppendPathDelim(TrimFilename(Filename));
|
|
for i:=0 to AComboBox.Items.Count-1 do begin
|
|
if CompareFilenames(Filename,AComboBox.Items[i])=0 then begin
|
|
// move to front (but not top, top should be the last used directory)
|
|
if i>2 then
|
|
AComboBox.Items.Move(i,1);
|
|
exit;
|
|
end;
|
|
end;
|
|
// insert in front (but not top, top should be the last used directory)
|
|
if AComboBox.Items.Count>0 then
|
|
i:=1
|
|
else
|
|
i:=0;
|
|
AComboBox.Items.Insert(i,Filename);
|
|
end;
|
|
|
|
var
|
|
SrcEdit: TSourceEditorInterface;
|
|
begin
|
|
SrcEdit := SourceEditorManagerIntf.ActiveEditor;
|
|
//DebugLn('TSourceNotebook.LoadFindInFilesHistory ',dbgsName(TextToFindComboBox),' ',dbgsName(FindHistory));
|
|
TextToFindComboBox.Items.Assign(InputHistories.FindHistory);
|
|
ReplaceTextComboBox.Items.Assign(InputHistories.ReplaceHistory);
|
|
if not EditorOpts.FindTextAtCursor then begin
|
|
if TextToFindComboBox.Items.Count>0 then begin
|
|
//debugln('TSourceNotebook.LoadFindInFilesHistory A TextToFindComboBox.Text=',TextToFindComboBox.Text);
|
|
TextToFindComboBox.ItemIndex:=0;
|
|
TextToFindComboBox.SelectAll;
|
|
//debugln('TSourceNotebook.LoadFindInFilesHistory B TextToFindComboBox.Text=',TextToFindComboBox.Text);
|
|
end;
|
|
end;
|
|
// show last used directories and directory of current file
|
|
AssignToComboBox(DirectoriesComboBox, InputHistories.FindInFilesPathHistory);
|
|
if (SrcEdit<>nil) and (FilenameIsAbsolute(SrcEdit.FileName)) then
|
|
AddFileToComboBox(DirectoriesComboBox, ExtractFilePath(SrcEdit.FileName));
|
|
if DirectoriesComboBox.Items.Count>0 then
|
|
DirectoriesComboBox.Text:=DirectoriesComboBox.Items[0];
|
|
// show last used file masks
|
|
AssignToComboBox(FileMaskComboBox, InputHistories.FindInFilesMaskHistory);
|
|
Options := InputHistories.FindInFilesSearchOptions;
|
|
SynSearchOptions := InputHistories.FindOptions * SharedOptions;//share basic options with FindReplaceDlg
|
|
end;
|
|
|
|
procedure TLazFindInFilesDialog.SaveHistory;
|
|
var
|
|
Dir: String;
|
|
begin
|
|
if ReplaceCheckBox.Checked then
|
|
InputHistories.AddToReplaceHistory(ReplaceText);
|
|
InputHistories.AddToFindHistory(FindText);
|
|
Dir:=AppendPathDelim(TrimFilename(DirectoriesComboBox.Text));
|
|
if Dir<>'' then
|
|
InputHistories.AddToFindInFilesPathHistory(Dir);
|
|
InputHistories.AddToFindInFilesMaskHistory(FileMaskComboBox.Text);
|
|
InputHistories.FindInFilesSearchOptions:=Options;
|
|
InputHistories.FindOptions := InputHistories.FindOptions - SharedOptions + (SynSearchOptions*SharedOptions);//share basic options with FindReplaceDlg
|
|
InputHistories.Save;
|
|
end;
|
|
|
|
procedure TLazFindInFilesDialog.FindInSearchPath(SearchPath: string);
|
|
begin
|
|
debugln(['TLazFindInFilesDialog.FindInSearchPath ',SearchPath]);
|
|
InitFindText;
|
|
LoadHistory;
|
|
DirectoriesComboBox.Text:=SearchPath;
|
|
WhereRadioGroup.ItemIndex:=ItemIndDirectories;
|
|
// disable replace. Find in files is often called,
|
|
// but almost never to replace with the same parameters
|
|
Options := Options-[fifReplace,fifReplaceAll];
|
|
Execute;
|
|
end;
|
|
|
|
procedure TLazFindInFilesDialog.FindInFilesPerDialog(AProject: TProject);
|
|
begin
|
|
InitFindText;
|
|
FindInFiles(AProject, FindText);
|
|
end;
|
|
|
|
procedure TLazFindInFilesDialog.InitFindText;
|
|
var
|
|
TempEditor: TSourceEditorInterface;
|
|
NewFindText: String;
|
|
begin
|
|
NewFindText:='';
|
|
TempEditor := SourceEditorManagerIntf.ActiveEditor;
|
|
if TempEditor <> nil
|
|
then //with TempEditor.EditorComponent do
|
|
begin
|
|
if EditorOpts.FindTextAtCursor
|
|
then begin
|
|
if TempEditor.SelectionAvailable and (TempEditor.BlockBegin.Y = TempEditor.BlockEnd.Y)
|
|
then NewFindText := TempEditor.Selection
|
|
else NewFindText := TSynEdit(TempEditor.EditorControl).GetWordAtRowCol(TempEditor.CursorTextXY);
|
|
end else begin
|
|
if InputHistories.FindHistory.Count>0 then
|
|
NewFindText:=InputHistories.FindHistory[0];
|
|
end;
|
|
end;
|
|
FindText:=NewFindText;
|
|
end;
|
|
|
|
procedure TLazFindInFilesDialog.InitFromLazSearch(Sender: TObject);
|
|
var
|
|
Dir: String;
|
|
begin
|
|
Dir:=AppendPathDelim(TrimFilename(TLazSearch(Sender).SearchDirectories));
|
|
if Dir<>'' then
|
|
DirectoriesComboBox.Text:= Dir;
|
|
Options:= TLazSearch(Sender).SearchOptions;
|
|
FileMaskComboBox.Text:= TLazSearch(Sender).SearchMask;
|
|
end;
|
|
|
|
procedure TLazFindInFilesDialog.FindInFiles(AProject: TProject; const AFindText: string);
|
|
begin
|
|
LazProject:=AProject;
|
|
LoadHistory;
|
|
|
|
// if there is no FindText, use the most recently used FindText
|
|
FindText:= AFindText;
|
|
if (FindText = '') and (InputHistories.FindHistory.Count > 0) then
|
|
FindText := InputHistories.FindHistory[0];
|
|
|
|
// disable replace. Find in files is often called,
|
|
// but almost never to replace with the same parameters
|
|
Options := Options-[fifReplace,fifReplaceAll];
|
|
Execute;
|
|
end;
|
|
|
|
function TLazFindInFilesDialog.GetResolvedDirectories: string;
|
|
begin
|
|
Result:=DirectoriesComboBox.Text;
|
|
IDEMacros.SubstituteMacros(Result);
|
|
Result:=TrimSearchPath(Result,GetBaseDirectory,true,true);
|
|
end;
|
|
|
|
function TLazFindInFilesDialog.Execute: boolean;
|
|
var
|
|
SearchForm: TSearchProgressForm;
|
|
begin
|
|
if ShowModal=mrOk then
|
|
begin
|
|
Result:=true;
|
|
SaveHistory;
|
|
|
|
SearchForm:= TSearchProgressForm.Create(SearchResultsView);
|
|
with SearchForm do begin
|
|
SearchOptions := self.Options;
|
|
SearchText := self.FindText;
|
|
ReplaceText := self.ReplaceText;
|
|
SearchMask := self.FileMaskComboBox.Text;
|
|
SearchDirectories := self.GetResolvedDirectories;
|
|
end;
|
|
|
|
try
|
|
if FindText <> '' then
|
|
begin
|
|
case WhereRadioGroup.ItemIndex of
|
|
ItemIndProject :
|
|
if LazProject=nil then
|
|
SearchForm.DoSearchProject(Project1)
|
|
else
|
|
SearchForm.DoSearchProject(LazProject);
|
|
ItemIndOpenFiles : SearchForm.DoSearchOpenFiles;
|
|
ItemIndDirectories: SearchForm.DoSearchDir;
|
|
ItemIndActiveFile : SearchForm.DoSearchActiveFile;
|
|
end;
|
|
end;
|
|
finally
|
|
FreeAndNil(SearchForm);
|
|
end;
|
|
end else
|
|
Result:=false;
|
|
|
|
FProject:=nil;
|
|
end;
|
|
|
|
end.
|
|
|