{ /*************************************************************************** SearchFrm.pas ------------------- ***************************************************************************/ *************************************************************************** * * * This source is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This code is distributed in the hope that it will be useful, but * * WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * * General Public License for more details. * * * * A copy of the GNU General Public License is available on the World * * Wide Web at . You can also * * obtain it by writing to the Free Software Foundation, * * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. * * * *************************************************************************** } unit SearchFrm; {$mode objfpc}{$H+} interface uses // RTL + FCL Classes, SysUtils, types, RegExpr, // LCL LCLIntf, Forms, Controls, ComCtrls, Dialogs, ExtCtrls, StdCtrls, Buttons, // CodeTools SourceLog, KeywordFuncLists, BasicCodeTools, FileProcs, // LazUtils FileUtil, LazFileUtils, LazFileCache, LazTracer, // IDEIntf IDEWindowIntf, LazIDEIntf, SrcEditorIntf, IDEDialogs, ProjectGroupIntf, // ide LazarusIDEStrConsts, InputHistory, IDEProcs, SearchResultView, Project; type { TSearchProgressForm } TSearchProgressForm = class(TForm) btnCancel: TBitBtn; MatchesLabel: TLABEL; SearchingLabel: TLABEL; SearchTextLabel: TLABEL; lblMatches: TLABEL; lblProgress: TLABEL; lblSearchText: TLABEL; Panel2: TPANEL; procedure FormClose(Sender: TObject; var {%H-}CloseAction: TCloseAction); procedure FormShow(Sender: TObject); procedure OnAddMatch(const Filename: string; const StartPos, EndPos: TPoint; const Lines: string); procedure SearchFormCREATE(Sender: TObject); procedure SearchFormDESTROY(Sender: TObject); procedure btnAbortCLICK(Sender: TObject); private fFlags: TSrcEditSearchOptions; fAbortString: string; fMask: string; fMatches: longint; fPad: string; FProgress: TIDESearchInTextProgress; fPromptOnReplace: boolean; fRecursive: boolean; FReplaceText: string; fResultsListUpdating: boolean; fResultsList: TStrings; fResultsWindow: TTabSheet; fSearchFileList: TStringList; fSearchFiles: boolean; fSearchFor: String; fDirectories: string; fSearchOpen: boolean; fSearchActive: boolean; fSearchProject: boolean; fSearchProjectGroup: boolean; fAborting: boolean; fLastUpdateProgress: DWORD; fWasActive: boolean; procedure DoFindInFiles(ADirectories: string); procedure DoFindInSearchList; procedure SetResultsList(const AValue: TStrings); procedure UpdateMatches; procedure UpdateProgress(FileName: string); function PadAndShorten(FileName: string): string; procedure SetOptions(TheOptions: TLazFindInFileSearchOptions); function GetOptions: TLazFindInFileSearchOptions; procedure SearchFile(const aFilename: string); procedure SetFlag(Flag: TSrcEditSearchOption; AValue: boolean); procedure DoSearchAndAddToSearchResults; function DoSearch: integer; public procedure DoSearchOpenFiles; procedure DoSearchActiveFile; procedure DoSearchDirs; procedure DoSearchProject(AProject: TProject); procedure DoSearchProjectGroup; public property SearchDirectories: string read fDirectories write fDirectories; property SearchText: string read fSearchFor write fSearchFor; property ReplaceText: string read FReplaceText write FReplaceText; property SearchOptions: TLazFindInFileSearchOptions read GetOptions write SetOptions; property SearchFileList: TStringList read fSearchFileList write fSearchFileList; property ResultsList: TStrings read fResultsList write SetResultsList; property SearchMask: string read fMask write fMask; property Pad: string read fPad write fPad; property ResultsWindow: TTabSheet read fResultsWindow write fResultsWindow; property PromptOnReplace: boolean read fPromptOnReplace write fPromptOnReplace;// this is asked once and can be changed when prompting property Progress: TIDESearchInTextProgress read FProgress; end; var SearchProgressForm: TSearchProgressForm; function SearchInText(const TheFileName: string; var TheText: string;// if TheFileName='' then use TheText SearchFor, ReplaceText: string; Flags: TSrcEditSearchOptions; var Prompt: boolean; Progress: TIDESearchInTextProgress = nil ): TModalResult; function TrimLinesAndAdjustPos(const Lines: string; var APosition: integer): string; function SearchInLine(const SearchStr: string; SrcLog: TSourceLog; LineNumber: integer; WholeWords: boolean; StartInLine: integer; out MatchStartInLine: integer): boolean; implementation {$R *.lfm} const WordBreakChars = [#0..#31,'.', ',', ';', ':', '"', '''', '!', '?', '[', ']', '(', ')', '{', '}', '^', '-', '=', '+', '*', '/', '\', '|', ' ']; WhiteSpaceChars = [' ',#10,#13,#9]; function SearchInLine(const SearchStr: string; SrcLog: TSourceLog; LineNumber: integer; WholeWords: boolean; StartInLine: integer; out MatchStartInLine: integer): boolean; // search SearchStr in SrcLog line // returns MatchStartInLine=1 for start of line var LineRange: TLineRange; Src: String; StartPos: PChar; EndPos: PChar; i: Integer; SearchLen: Integer; LineStartPos: PChar; FirstChar: Char; Found: Boolean; CharInFront: PChar; CharBehind: PChar; begin Result:=false; if SearchStr='' then exit; SrcLog.GetLineRange(LineNumber-1,LineRange); Src:=SrcLog.Source; SearchLen:=length(SearchStr); LineStartPos:=@Src[LineRange.StartPos]; StartPos:=LineStartPos+StartInLine-1; EndPos:=@Src[LineRange.EndPos-SearchLen+1]; FirstChar:=SearchStr[1]; while (StartPosSearchLen then begin Found:=true; MatchStartInLine:=StartPos-LineStartPos+1; if WholeWords then begin CharInFront:=StartPos-1; CharBehind:=StartPos+SearchLen; if ((MatchStartInLine=1) or (CharInFront^ in WordBreakChars)) and ((StartPos+SearchLen=@Src[LineRange.EndPos]) or (CharBehind^ in WordBreakChars)) then begin // word start and word end end else begin // not whole word Found:=false; end; end; if Found then begin Result:=true; exit; end; end; end; inc(StartPos); end; end; function TrimLinesAndAdjustPos(const Lines: string; var APosition: integer): string; var StartPos: Integer; EndPos: Integer; begin if Lines='' then begin Result:=''; exit; end; if LineEndCount(Lines)=0 then begin StartPos:=1; while (StartPos<=length(Lines)) and (Lines[StartPos] in WhiteSpaceChars) do inc(StartPos); if (APosition>0) and (StartPos>APosition) then StartPos:=APosition; EndPos:=length(Lines)+1; while (EndPos>=StartPos) and (Lines[EndPos-1] in WhiteSpaceChars) do dec(EndPos); dec(APosition,StartPos-1); Result:=copy(Lines,StartPos,EndPos-StartPos); end else Result:=Lines; end; function SearchInText(const TheFileName: string; var TheText: string;// if TheFileName='' then use TheText SearchFor, ReplaceText: string; Flags: TSrcEditSearchOptions; var Prompt: boolean; Progress: TIDESearchInTextProgress = nil ): TModalResult; var OriginalFile: TSourceLog;// The original File being searched CaseFile: TSourceLog; // The working File being searched FoundStartPos: TPoint; // Position of match in line. 1 based. FoundEndPos: TPoint; ReplaceLineOffset: integer;// number of lines added/deleted by replacement. LastReplaceLine: integer; // last changed line by replace. 1 based LastReplaceColOffset: integer;// bytes added/deleted by replace in last line TempSearch: string; // Temp Storage for the search string. RE: TRegExpr; Lines: String; SrcEditValid: Boolean;// true if SrcEdit is valid SrcEdit: TSourceEditorInterface; PaintLockEnabled: Boolean; ReplacedText: PChar; ReplacedTextCapacity: integer; ReplacedTextLength: integer; ReplacedTextOriginalPos: integer;// 1-based. e.g. 2 bytes has been replaced => ReplacedTextOriginalPos=3. procedure DoAbort; begin if Progress<>nil then Progress.Abort:=true; Result:=mrAbort; end; procedure ProcessMessages; begin if Application<>nil then Application.ProcessMessages; if (Progress<>nil) and Progress.Abort then Result:=mrAbort; end; function FileIsOpenInSourceEditor: boolean; begin if not SrcEditValid then begin if (TheFileName<>'') and (SourceEditorManagerIntf<>nil) then SrcEdit:=SourceEditorManagerIntf.SourceEditorIntfWithFilename(TheFileName) else SrcEdit:=nil; SrcEditValid:=true; end; Result:=SrcEdit<>nil; end; procedure GrowNewText(NewLength: integer); var NewCapacity: Integer; begin if NewLength<=ReplacedTextCapacity then exit; // grow // first double NewCapacity:=ReplacedTextCapacity*2; if NewLength>NewCapacity then begin // double is not enough, use the original size as minimum if NewCapacity<1 then NewCapacity:=OriginalFile.SourceLength+1000; if NewLength>NewCapacity then begin // still not enough -> grow to new length NewCapacity:=NewLength; end; end; ReplacedTextCapacity:=NewCapacity; ReAllocMem(ReplacedText,ReplacedTextCapacity); end; procedure EnablePaintLock; begin if (not PaintLockEnabled) and FileIsOpenInSourceEditor then begin PaintLockEnabled:=true; SrcEdit.BeginUpdate; end; end; procedure DisablePaintLock; begin if PaintLockEnabled then SrcEdit.EndUpdate; PaintLockEnabled:=false; end; procedure EndLocks; begin DisablePaintLock; SrcEditValid:=false; end; procedure DoReplaceLine; var AReplace: String; Action: TSrcEditReplaceAction; OriginalTextPos: integer; // 1-based GapLength: Integer; NewLength: Integer; SrcEditPosValid: boolean; SrcEditStartPos, SrcEditEndPos: TPoint; aLastLineLength: integer; aLineCount: integer; i: integer; procedure GetSrcEditPos; begin if not SrcEditPosValid then begin SrcEditStartPos:=FoundStartPos; SrcEditEndPos:=FoundEndPos; // FoundStart/EndPos contain the original position // add the changes due to replacement to SrcEditStart/EndPos if SrcEditStartPos.Y=LastReplaceLine then inc(SrcEditStartPos.X,LastReplaceColOffset); if SrcEditStartPos.Y>=LastReplaceLine then inc(SrcEditStartPos.Y,ReplaceLineOffset); if SrcEditEndPos.Y=LastReplaceLine then inc(SrcEditEndPos.X,LastReplaceColOffset); if SrcEditEndPos.Y>=LastReplaceLine then inc(SrcEditEndPos.Y,ReplaceLineOffset); SrcEditPosValid:=true; end; end; begin // create replacement AReplace:=ReplaceText; if sesoRegExpr in Flags then AReplace:=RE.Substitute(AReplace); //DebugLn(['DoReplaceLine Replace with "',AReplace,'"']); SrcEditPosValid:=false; // ask the user if Prompt and (TheFileName<>'') then begin // open the place in the source editor EndLocks; // update windows ProcessMessages; if Result=mrAbort then exit; GetSrcEditPos; if LazarusIDE.DoOpenFileAndJumpToPos(TheFileName,SrcEditStartPos, -1,-1,-1,[ofUseCache,ofDoNotLoadResource,ofVirtualFile,ofRegularFile]) <>mrOk then begin DoAbort; exit; end; // select found text if not FileIsOpenInSourceEditor then RaiseGDBException('inconsistency'); SrcEdit.SelectText(SrcEditStartPos.Y,SrcEditStartPos.X, SrcEditEndPos.Y,SrcEditEndPos.X); SrcEdit.AskReplace(nil,SrcEdit.Selection,AReplace, SrcEditStartPos.Y,SrcEditStartPos.X,Action); case Action of seraSkip: exit; seraReplace: ; seraReplaceAll: Prompt:=false; else DoAbort; exit; end; end; if FileIsOpenInSourceEditor then begin // change text in source editor EnablePaintLock; GetSrcEditPos; SrcEdit.SelectText(SrcEditStartPos.Y,SrcEditStartPos.X, SrcEditEndPos.Y,SrcEditEndPos.X); SrcEdit.Selection:=AReplace; // count total replacements and adjust offsets aLineCount:=LineEndCount(AReplace,aLastLineLength); //debugln(['DoReplaceLine Replace="',dbgstr(AReplace),'" aLineCount=',aLineCount,' aLastLineLength=',aLastLineLength]); if aLineCount>0 then begin // replaced with multiple lines LastReplaceColOffset:=aLastLineLength+1-FoundEndPos.X; end else begin if FoundStartPos.Y<>LastReplaceLine then LastReplaceColOffset:=0; // replaced with some words if FoundStartPos.Y=FoundEndPos.Y then begin // replaced some words with some words inc(LastReplaceColOffset, aLastLineLength-(FoundEndPos.X-FoundStartPos.X)); end else begin // replaced several lines with some words inc(LastReplaceColOffset,FoundStartPos.X+aLastLineLength-FoundEndPos.X); end; end; LastReplaceLine:=FoundEndPos.Y; Lines := ''; for i := SrcEditStartPos.Y to SrcEditStartPos.Y + aLineCount do Lines := Lines + SrcEdit.Lines[i-1] + LineEnding; Lines:=ChompOneLineEndAtEnd(Lines); if (Progress<>nil) and (Progress.OnAddMatch<>nil) then begin Progress.OnAddMatch(TheFileName, Point(FoundStartPos.x, FoundStartPos.y + ReplaceLineOffset), SrcEdit.CursorTextXY,Lines); end; inc(ReplaceLineOffset,aLineCount-(FoundEndPos.Y-FoundStartPos.Y)); //DebugLn(['DoReplaceLine FoundStartPos=',dbgs(FoundStartPos),' FoundEndPos=',dbgs(FoundEndPos),' aLastLineLength=',aLastLineLength,' LastReplaceLine=',LastReplaceLine,' LastReplaceColOffset=',LastReplaceColOffset,' ReplaceLineOffset=',ReplaceLineOffset]); end else begin // change text in memory/disk OriginalFile.LineColToPosition(FoundStartPos.Y,FoundStartPos.X, OriginalTextPos); GapLength:=OriginalTextPos-ReplacedTextOriginalPos; NewLength:=ReplacedTextLength+GapLength+length(AReplace); GrowNewText(NewLength); // copy the text between the last replacement and this replacement if GapLength>0 then begin System.Move(OriginalFile.Source[ReplacedTextOriginalPos], ReplacedText[ReplacedTextLength],GapLength); inc(ReplacedTextLength,GapLength); end; // copy the replacement if AReplace<>'' then begin System.Move(AReplace[1],ReplacedText[ReplacedTextLength],length(AReplace)); inc(ReplacedTextLength,length(AReplace)); end; // save original position behind found position OriginalFile.LineColToPosition(FoundEndPos.Y,FoundEndPos.X, ReplacedTextOriginalPos); Lines:=copy(OriginalFile.GetLines(FoundStartPos.Y,FoundStartPos.Y), 1, FoundStartPos.X - 1) + AReplace + copy(OriginalFile.GetLines(FoundEndPos.Y,FoundEndPos.Y), FoundEndPos.x, MaxInt); Lines:=ChompOneLineEndAtEnd(Lines); aLineCount:=LineEndCount(AReplace,aLastLineLength); if aLineCount = 0 then aLastLineLength := aLastLineLength + FoundStartPos.X; if (Progress<>nil) and (Progress.OnAddMatch<>nil) then begin Progress.OnAddMatch(TheFileName, Point(FoundStartPos.x, FoundStartPos.y + ReplaceLineOffset), Point(aLastLineLength, FoundStartPos.Y + aLineCount + ReplaceLineOffset), Lines); end; inc(ReplaceLineOffset,aLineCount-(FoundEndPos.Y-FoundStartPos.Y)); end; end; procedure CommitChanges; var GapLength: Integer; NewLength: Integer; NewText: string; CurResult: TModalResult; begin EndLocks; if (ReplacedText<>nil) then begin if SearchInText<>mrAbort then begin GapLength:=OriginalFile.SourceLength+1-ReplacedTextOriginalPos; NewLength:=ReplacedTextLength+GapLength; GrowNewText(NewLength); // copy the text between the last and this replacement if GapLength>0 then begin System.Move(OriginalFile.Source[ReplacedTextOriginalPos], ReplacedText[ReplacedTextLength],GapLength); inc(ReplacedTextLength,GapLength); end; SetLength(NewText,ReplacedTextLength); if NewText<>'' then System.Move(ReplacedText[0],NewText[1],length(NewText)); if (TheFileName<>'') then begin OriginalFile.Source:=NewText; if (not OriginalFile.SaveToFile(TheFileName)) then begin CurResult:=MessageDlg(lisCodeToolsDefsWriteError, Format(lisErrorWritingFile, [TheFileName]), mtError,[mbCancel,mbAbort],0); if CurResult=mrAbort then DoAbort; end; end else begin TheText:=NewText; end; end; FreeMem(ReplacedText); end; end; var Found: Boolean; Src: String; NewMatchStartPos: PtrInt; NewMatchEndPos: PtrInt; begin //debugln(['SearchInText TheFileName=',TheFileName,' SearchFor=',SearchFor,'" ReplaceText=',ReplaceText,'"']); if (Progress<>nil) and Progress.Abort then exit(mrAbort); Result:=mrOk; OriginalFile:=nil; CaseFile:=nil; RE:=nil; SrcEdit:=nil; SrcEditValid:=false; PaintLockEnabled:=false; ReplacedText:=nil; ReplacedTextCapacity:=0; ReplacedTextLength:=0; ReplacedTextOriginalPos:=1; ReplaceLineOffset:=0; LastReplaceLine:=0; LastReplaceColOffset:=0; try FoundEndPos:= Point(0,0); TempSearch:= SearchFor; // load text (to save memory, do not use codetools cache system) if FileIsOpenInSourceEditor then begin OriginalFile:=TSourceLog.Create(SrcEdit.GetText(false)); end else if TheFileName<>'' then begin OriginalFile:=TSourceLog.Create(''); OriginalFile.LoadFromFile(TheFileName); end else begin OriginalFile:=TSourceLog.Create(TheText); end; if OriginalFile.Source='' then exit; CaseFile:=nil; if sesoRegExpr in Flags then begin // Setup the regular expression search engine RE:=TRegExpr.Create; RE.ModifierI:=not (sesoMatchCase in Flags); RE.ModifierM:=true; RE.ModifierS:=sesoMultiLine in Flags; Src:=OriginalFile.Source; if sesoWholeWord in Flags then RE.Expression:='\b'+SearchFor+'\b' else RE.Expression:=SearchFor; end else begin // convert case if necessary if not (sesoMatchCase in Flags) then begin CaseFile:=TSourceLog.Create(UpperCaseStr(OriginalFile.Source)); TempSearch:=UpperCaseStr(TempSearch); Src:=CaseFile.Source; end else Src:=OriginalFile.Source; end; //debugln(['TheFileName=',TheFileName,' len=',OriginalFile.SourceLength,' Cnt=',OriginalFile.LineCount,' TempSearch=',TempSearch]); NewMatchEndPos:=1; repeat Found:=false; if sesoRegExpr in Flags then begin // search the text for regular expression RE.InputString:=Src; if RE.ExecPos(NewMatchEndPos) then begin Found:=true; NewMatchStartPos:=RE.MatchPos[0]; NewMatchEndPos:=NewMatchStartPos+RE.MatchLen[0]; end; end else begin // search for normal text if SearchNextInText(PChar(TempSearch),length(TempSearch), PChar(Src),length(Src), NewMatchEndPos-1,NewMatchStartPos,NewMatchEndPos, sesoWholeWord in Flags,sesoMultiLine in Flags) then begin Found:=true; inc(NewMatchStartPos); inc(NewMatchEndPos); end; end; if Found then begin // found => convert position, report and/or replace OriginalFile.AbsoluteToLineCol(NewMatchStartPos, FoundStartPos.Y,FoundStartPos.X); OriginalFile.AbsoluteToLineCol(NewMatchEndPos, FoundEndPos.Y,FoundEndPos.X); //DebugLn(['SearchInText NewMatchStartPos=',NewMatchStartPos,' NewMatchEndPos=',NewMatchEndPos,' FoundStartPos=',dbgs(FoundStartPos),' FoundEndPos=',dbgs(FoundEndPos),' Found="',dbgstr(copy(Src,NewMatchStartPos,NewMatchEndPos-NewMatchStartPos)),'" Replace=',sesoReplace in Flags]); if sesoReplace in Flags then begin DoReplaceLine end else begin if (Progress<>nil) and (Progress.OnAddMatch<>nil) then begin Lines:=OriginalFile.GetLines(FoundStartPos.Y,FoundEndPos.Y); Lines:=ChompOneLineEndAtEnd(Lines); if (Progress<>nil) and (Progress.OnAddMatch<>nil) then begin Progress.OnAddMatch(TheFileName,FoundStartPos,FoundEndPos,Lines); end; end; end; end else begin // not found break; end; // check abort if (Result=mrAbort) then begin exit; end; until false; finally CommitChanges; if OriginalFile=CaseFile then CaseFile:=nil; FreeAndNil(OriginalFile); FreeAndNil(CaseFile); FreeAndNil(RE); end; end;//SearchFile { TSearchProgressForm } procedure TSearchProgressForm.btnAbortCLICK(Sender: TObject); begin Progress.Abort:= true; end; procedure TSearchProgressForm.SearchFormCREATE(Sender: TObject); Function MaxWidth(const Labs : array of TLabel) : integer; var i,w : integer; begin Result:=0; for i:=low(Labs) to high(Labs) do begin w:=Canvas.TextWidth(Labs[i].Caption); if Result lblProgress.Parent.ClientWidth-12 do begin ShorterFileName:= PadAndShorten(DisplayFileName); if ShorterFileName=DisplayFileName then break; DisplayFileName:=ShorterFileName; //DebugLn(['TSearchForm.UpdateProgress Padded DisplayFileName="',dbgstr(DisplayFileName),'"']); end; lblProgress.Caption := DisplayFileName; Application.ProcessMessages; end; procedure TSearchProgressForm.SearchFile(const aFilename: string); var Src: String; begin fResultsList.BeginUpdate; try Src:=''; SearchInText(aFilename,Src,fSearchFor,FReplaceText,FFlags, fPromptOnReplace,Progress); finally fResultsList.EndUpdate; end; end; procedure TSearchProgressForm.SetFlag(Flag: TSrcEditSearchOption; AValue: boolean); begin if AValue then Include(fFlags,Flag) else Exclude(fFlags,Flag); end; procedure TSearchProgressForm.DoSearchAndAddToSearchResults; var ListPage: TTabSheet; Cnt: integer; State: TIWGetFormState; begin Cnt:= 0; LazarusIDE.DoShowSearchResultsView(iwgfShow); ListPage:=SearchResultsView.AddSearch(SearchText,SearchText, ReplaceText,SearchDirectories,SearchMask,SearchOptions); try (* BeginUpdate prevents ListPage from being closed, other pages can still be closed or inserted, so PageIndex can change *) SearchResultsView.BeginUpdate(ListPage.PageIndex); ResultsList:= SearchResultsView.Items[ListPage.PageIndex]; ResultsList.Clear; ResultsWindow:= ListPage; try Show; // floating window, not dockable Cnt:= DoSearch; except on E: ERegExpr do IDEMessageDialog(lisUEErrorInRegularExpression, E.Message,mtError, [mbCancel]); end; finally ListPage.Caption:= Format('%s (%d)',[ListPage.Caption,Cnt]); SearchResultsView.EndUpdate(ListPage.PageIndex); // show, but bring to front only if Search Progress dialog was active if fWasActive then State:=iwgfShowOnTop else State:=iwgfShow; LazarusIDE.DoShowSearchResultsView(State); end; end; procedure TSearchProgressForm.DoSearchOpenFiles; var i: integer; TheFileList: TStringList; SrcEdit: TSourceEditorInterface; begin try TheFileList:= TStringList.Create; for i:= 0 to SourceEditorManagerIntf.UniqueSourceEditorCount -1 do begin //only if file exists on disk SrcEdit := SourceEditorManagerIntf.UniqueSourceEditors[i]; if FilenameIsAbsolute(SrcEdit.FileName) and (not FileExistsCached(SrcEdit.FileName)) then continue; TheFileList.Add(SrcEdit.FileName); end; SearchFileList:= TheFileList; DoSearchAndAddToSearchResults; finally FreeAndNil(TheFileList); end; end; procedure TSearchProgressForm.DoSearchActiveFile; var TheFileList: TStringList; begin try TheFileList:= TStringList.Create; // Add a single file to the list TheFileList.Add(SourceEditorManagerIntf.ActiveEditor.FileName); SearchFileList:= TheFileList; DoSearchAndAddToSearchResults; finally FreeAndNil(TheFileList); end; end; procedure TSearchProgressForm.DoSearchDirs; begin SearchFileList:= Nil; DoSearchAndAddToSearchResults; end; procedure TSearchProgressForm.DoSearchProject(AProject: TProject); var AnUnitInfo: TUnitInfo; TheFileList: TStringList; begin try TheFileList:= TStringList.Create; AnUnitInfo:=AProject.FirstPartOfProject; while AnUnitInfo<>nil do begin //Only if file exists on disk. if FilenameIsAbsolute(AnUnitInfo.FileName) and FileExistsCached(AnUnitInfo.FileName) then TheFileList.Add(AnUnitInfo.FileName); AnUnitInfo:=AnUnitInfo.NextPartOfProject; end; SearchFileList:= TheFileList; DoSearchAndAddToSearchResults; finally FreeAndNil(TheFileList); end; end; procedure TSearchProgressForm.DoSearchProjectGroup; begin if (ProjectGroupManager=nil) or (ProjectGroupManager.CurrentProjectGroup=nil) then DoSearchProject(Project1) else begin SearchFileList:= Nil; SearchDirectories:=ProjectGroupManager.GetSrcPaths; DoSearchAndAddToSearchResults; end; end; function TSearchProgressForm.PadAndShorten(FileName: string): string; var FoundAt: integer; begin FoundAt:= System.Pos(PathDelim,FileName); if FoundAt<1 then begin Result := Filename; end else begin Result:= fPad + copy(FileName,FoundAt+1,Length(FileName)); end; end;//PadAndShorten end.