{ ***************************************************************************** See the file COPYING.modifiedLGPL.txt, included in this distribution, for details about the license. ***************************************************************************** Author: Mattias Gaertner Abstract: Defines a converter and tools to modify a Text. The Text can be a file, a string or a TStrings. Packages can register extra tools, which the IDE user can then be put together to define a series of changes. For example several Find&Replace tools can be added and then executed automatically. For an extensive example, see the package in components/h2pas/. } unit IDETextConverter; {$mode objfpc}{$H+} interface uses Classes, SysUtils, TypInfo, // LCL LCLProc, // LazUtils UITypes, FileUtil, LazFileUtils, LazUTF8, LazLoggerBase, // IdeIntf SrcEditorIntf, PropEdits, ObjInspStrConsts; type TCustomTextConverterTool = class; TTextConverterType = ( tctSource, tctFile, tctStrings, tctCodeBuffer // TCodeBuffer ); { TIDETextConverter A component to hold a Text and tools to change the Text. For example to do several find and replace operations on the text. The Text can be a file, a string, TStrings or a TCodeBuffer. The Text is converted on the fly, whenever someone reads/write one of the formats. The tools are decendants of TCustomTextConverterTool. } TIDETextConverter = class(TComponent) private FFilename: string; FSource: string; FCodeBuffer: Pointer; FStrings: TStrings; FCurrentType: TTextConverterType; FFileIsTemporary: boolean; FStringsIsTemporary: Boolean; procedure CreateTempFilename; function GetCodeBuffer: Pointer; function GetFilename: string; function GetSource: string; function GetStrings: TStrings; procedure ResetStrings; procedure ResetFile; procedure ConvertToFile(const NewFilename: string); procedure SetCodeBuffer(const AValue: Pointer); procedure SetFilename(const AValue: string); procedure SetSource(const AValue: string); procedure SetStrings(const AValue: TStrings); procedure SetCurrentType(const AValue: TTextConverterType); procedure SetFileIsTemporary(const AValue: boolean); procedure SetStringsIsTemporary(const AValue: Boolean); protected function GetTempFilename: string; virtual; public constructor Create(TheOwner: TComponent); override; destructor Destroy; override; procedure Clear; procedure CheckType(aTextType: TTextConverterType); function SupportsType(aTextType: TTextConverterType): boolean; virtual; function Execute(ToolList: TComponent; out ErrorTool: TComponent): TModalResult;// run the tools function LoadFromFile(const AFilename: string; UseIDECache: Boolean = true; UpdateFromDisk: Boolean = true; Revert: Boolean = false ): Boolean; virtual; procedure InitWithFilename(const AFilename: string); procedure InitWithSource(const ASource: string); procedure InitWithStrings(const aStrings: TStrings); procedure InitWithCodeBuffers(const aBuffer: Pointer); property CurrentType: TTextConverterType read FCurrentType write SetCurrentType; property Source: string read GetSource write SetSource; property CodeBuffer: Pointer read GetCodeBuffer write SetCodeBuffer; property Filename: string read GetFilename write SetFilename; property Strings: TStrings read GetStrings write SetStrings; property FileIsTemporary: boolean read FFileIsTemporary write SetFileIsTemporary; property StringsIsTemporary: Boolean read FStringsIsTemporary write SetStringsIsTemporary; end; { TCustomTextConverterTool An abstract component to change a Text (TIDETextConverter). } TCustomTextConverterTool = class(TComponent) private FCaption: string; FDescription: string; FEnabled: boolean; FErrorColumn: integer; FErrorFilename: string; FErrorLine: integer; FErrorMsg: string; FErrorTopLine: integer; function IsCaptionStored: boolean; procedure SetCaption(const AValue: string); procedure SetDescription(const AValue: string); public class function ClassDescription: string; virtual; abstract;//the first line should be a short title class function FirstLineOfClassDescription: string; constructor Create(TheOwner: TComponent); override; function Execute(aText: TIDETextConverter): TModalResult; virtual; abstract; procedure Assign(Source: TPersistent); override; procedure ClearError; virtual; procedure AssignError(Source: TCustomTextConverterTool); procedure AssignCodeToolBossError; property ErrorMsg: string read FErrorMsg write FErrorMsg; property ErrorLine: integer read FErrorLine write FErrorLine; property ErrorColumn: integer read FErrorColumn write FErrorColumn; property ErrorTopLine: integer read FErrorTopLine write FErrorTopLine; property ErrorFilename: string read FErrorFilename write FErrorFilename; published property Caption: string read FCaption write SetCaption stored IsCaptionStored; property Description: string read FDescription write SetDescription; property Enabled: boolean read FEnabled write FEnabled default True; end; TCustomTextConverterToolClass = class of TCustomTextConverterTool; { TCustomTextReplaceTool A tool to do a 'find and replace' in a text. } TTextReplaceToolOption = ( trtMatchCase, // search case sensitive trtWholeWord, // search at word boundaries trtRegExpr, // use regular expressions for find and replace trtMultiLine // ignore type of line endings in pattern (e.g. #10 = #13#10) //TODO trtSearchInReplacement,// when replaced, continue search at start of replacement, instead of end of replacement //TODO trtReplaceUntilNotFound// restart replace until pattern not found ); TTextReplaceToolOptions = set of TTextReplaceToolOption; TCustomTextReplaceTool = class(TCustomTextConverterTool) private FOptions: TTextReplaceToolOptions; FReplaceWith: string; FSearchFor: string; procedure SetOptions(const AValue: TTextReplaceToolOptions); procedure SetReplaceWith(const AValue: string); procedure SetSearchFor(const AValue: string); public class function ClassDescription: string; override; function Execute(aText: TIDETextConverter): TModalResult; override; procedure Assign(Source: TPersistent); override; property SearchFor: string read FSearchFor write SetSearchFor; property ReplaceWith: string read FReplaceWith write SetReplaceWith; property Options: TTextReplaceToolOptions read FOptions write SetOptions; end; { TTextReplaceTool } TTextReplaceTool = class(TCustomTextReplaceTool) published property SearchFor; property ReplaceWith; property Options; end; { TTextConverterToolClasses A list to hold the registered TCustomTextConverterToolClass(es) } TTextConverterToolClasses = class private FItems: TFPList;// list of TCustomTextConverterToolClass function GetCount: integer; function GetItems(Index: integer): TCustomTextConverterToolClass; public constructor Create; destructor Destroy; override; procedure RegisterClass(AClass: TCustomTextConverterToolClass); procedure UnregisterClass(AClass: TCustomTextConverterToolClass); function FindByName(const aClassName: string): TCustomTextConverterToolClass; function FindByFirstLineOfClassDescription(const Line: string ): TCustomTextConverterToolClass; procedure FindClass(Reader: TReader; const aClassName: string; var ComponentClass: TComponentClass); property Items[Index: integer]: TCustomTextConverterToolClass read GetItems; default; property Count: integer read GetCount; function SupportsType(aTextType: TTextConverterType): boolean; virtual; abstract; function GetTempFilename: string; virtual; abstract; function LoadFromFile(Converter: TIDETextConverter; const AFilename: string; UpdateFromDisk, Revert: Boolean): Boolean; virtual; abstract; function SaveCodeBufferToFile(Converter: TIDETextConverter; const AFilename: string): Boolean; virtual; abstract; function GetCodeBufferSource(Converter: TIDETextConverter; out Source: string): boolean; virtual; abstract; function CreateCodeBuffer(Converter: TIDETextConverter; const Filename, NewSource: string; out CodeBuffer: Pointer): boolean; virtual; abstract; function LoadCodeBufferFromFile(Converter: TIDETextConverter; const Filename: string; UpdateFromDisk, Revert: Boolean; out CodeBuffer: Pointer): boolean; virtual; abstract; procedure AssignCodeToolBossError(Target: TCustomTextConverterTool); virtual; end; var TextConverterToolClasses: TTextConverterToolClasses = nil;// set by the IDE procedure ClearTextConverterToolList(List: TComponent); procedure CopyTextConverterToolList(Src, Dest: TComponent; ClearDest: boolean = true); procedure MakeToolNameUnique(List: TComponent; NewTool: TCustomTextConverterTool); procedure MakeToolCaptionUnique(List: TComponent; NewTool: TCustomTextConverterTool); procedure MakeToolCaptionAndNameUnique(List: TComponent; NewTool: TCustomTextConverterTool); function AddNewTextConverterTool(List: TComponent; NewClass: TCustomTextConverterToolClass): TCustomTextConverterTool; implementation procedure MakeToolNameUnique(List: TComponent; NewTool: TCustomTextConverterTool); var NewName: String; procedure MakeValidIdentifier; var i: Integer; begin for i:=length(NewName) downto 1 do if not (NewName[i] in ['0'..'9','_','a'..'z','A'..'Z']) then System.Delete(NewName,i,1); if (NewName<>'') and (NewName[1] in ['0'..'9']) then NewName:='_'+NewName; end; function NameIsUnique: Boolean; var i: Integer; CurTool: TCustomTextConverterTool; begin MakeValidIdentifier; if NewName='' then exit(false); for i:=0 to List.ComponentCount-1 do begin CurTool:=TCustomTextConverterTool(List.Components[i]); if CurTool=NewTool then continue; if CompareText(CurTool.Name,NewName)=0 then exit(false); end; Result:=true; NewTool.Name:=NewName; end; begin NewName:=NewTool.Name; if NameIsUnique then exit; NewName:=NewTool.FirstLineOfClassDescription; if NewName='' then NewName:=NewTool.ClassName; while not NameIsUnique do NewName:=CreateNextIdentifier(NewName); end; procedure MakeToolCaptionUnique(List: TComponent; NewTool: TCustomTextConverterTool); var NewCaption: String; function CaptionIsUnique: Boolean; var i: Integer; CurTool: TCustomTextConverterTool; begin if NewCaption='' then exit(false); for i:=0 to List.ComponentCount-1 do begin CurTool:=TCustomTextConverterTool(List.Components[i]); if CurTool=NewTool then continue; if CompareText(CurTool.Caption,NewCaption)=0 then exit(false); end; Result:=true; NewTool.Caption:=NewCaption; end; begin NewCaption:=NewTool.Caption; if CaptionIsUnique then exit; NewCaption:=NewTool.FirstLineOfClassDescription; if NewCaption='' then NewCaption:=NewTool.ClassName; while not CaptionIsUnique do NewCaption:=CreateNextIdentifier(NewCaption); end; procedure MakeToolCaptionAndNameUnique(List: TComponent; NewTool: TCustomTextConverterTool); begin MakeToolNameUnique(List,NewTool); MakeToolCaptionUnique(List,NewTool); end; function AddNewTextConverterTool(List: TComponent; NewClass: TCustomTextConverterToolClass): TCustomTextConverterTool; begin Result:=NewClass.Create(List); MakeToolCaptionAndNameUnique(List,Result); if Result.Caption='' then RaiseGDBException(''); end; procedure ClearTextConverterToolList(List: TComponent); begin if List=nil then exit; while List.ComponentCount>0 do List.Components[List.ComponentCount-1].Free; end; procedure CopyTextConverterToolList(Src, Dest: TComponent; ClearDest: boolean); var i: Integer; SrcTool: TCustomTextConverterTool; NewTool: TCustomTextConverterTool; begin if ClearDest then ClearTextConverterToolList(Dest); for i:=0 to Src.ComponentCount-1 do begin SrcTool:=Src.Components[i] as TCustomTextConverterTool; NewTool:=TCustomTextConverterToolClass(SrcTool.ClassType).Create(Dest); NewTool.Assign(SrcTool); NewTool.Name:=SrcTool.Name; end; end; { TIDETextConverter } procedure TIDETextConverter.SetFilename(const AValue: string); begin ConvertToFile(AValue); end; function TIDETextConverter.GetFilename: string; begin CurrentType:=tctFile; Result:=FFilename; end; function TIDETextConverter.GetSource: string; begin CurrentType:=tctSource; Result:=FSource; end; function TIDETextConverter.GetStrings: TStrings; begin CurrentType:=tctStrings; Result:=FStrings; end; procedure TIDETextConverter.ResetStrings; begin if StringsIsTemporary then FStrings.Free; FStrings:=nil; FStringsIsTemporary:=false; end; procedure TIDETextConverter.ResetFile; begin if FileIsTemporary then begin DeleteFileUTF8(FFilename); // do not change FFileIsTemporary, so that File > Source > File sequences // keep the file temporary. end; end; procedure TIDETextConverter.SetSource(const AValue: string); begin FCurrentType:=tctSource; ResetStrings; ResetFile; FSource:=AValue; end; procedure TIDETextConverter.SetStrings(const AValue: TStrings); begin FCurrentType:=tctStrings; ResetFile; ResetStrings; FStrings:=AValue; end; procedure TIDETextConverter.SetCurrentType(const AValue: TTextConverterType); var fs: TFileStream; begin if FCurrentType=AValue then exit; CheckType(AValue); //DebugLn(['TIDETextConverter.SetCurrentType ',ord(FCurrentType),' ',ord(AValue)]); case AValue of tctSource: // convert to Source begin FSource:=''; case FCurrentType of tctStrings: if FStrings<>nil then begin FSource:=FStrings.Text; ResetStrings; end; tctFile: if FileExistsUTF8(FFilename) then begin fs:=TFileStream.Create(FFilename,fmOpenRead); try SetLength(FSource,fs.Size); fs.Read(FSource[1],length(FSource)); finally fs.Free; end; ResetFile; end; tctCodeBuffer: begin TextConverterToolClasses.GetCodeBufferSource(Self,FSource); FCodeBuffer:=nil; end; end; end; tctStrings: // convert to TStrings begin if FStrings<>nil then RaiseGDBException('TTextConverterText.SetCurrentType FStrings<>nil'); FStrings:=TStringList.Create; fStringsIsTemporary:=true; case FCurrentType of tctSource: begin FStrings.Text:=FSource; FSource:=''; end; tctFile: if FileExistsUTF8(FFilename) then begin FStrings.LoadFromFile(FFilename); ResetFile; end; tctCodeBuffer: begin TextConverterToolClasses.GetCodeBufferSource(Self,FSource); FStrings.Text:=FSource; FSource:=''; FCodeBuffer:=nil; end; end; end; tctFile: // convert to File begin // keep old Filename, so that a Filename, Source, Filename combination // uses the same Filename if FFilename='' then CreateTempFilename; case FCurrentType of tctSource: begin fs:=TFileStream.Create(FFilename,fmCreate); try if FSource<>'' then begin fs.Write(FSource[1],length(FSource)); FSource:=''; end; finally fs.Free; end; end; tctStrings: if FStrings<>nil then begin FStrings.LoadFromFile(FFilename); ResetStrings; end; tctCodeBuffer: begin TextConverterToolClasses.SaveCodeBufferToFile(Self,FFilename); FCodeBuffer:=nil; end; end; end; tctCodeBuffer: // convert to CodeBuffer begin // keep old Filename, so that a Filename, Source, Filename combination // uses the same Filename if FFilename='' then CreateTempFilename; case FCurrentType of tctSource: begin TextConverterToolClasses.CreateCodeBuffer(Self,FFilename,FSource, FCodeBuffer); FSource:=''; end; tctStrings: begin TextConverterToolClasses.CreateCodeBuffer(Self,FFilename, FStrings.Text,FCodeBuffer); ResetStrings; end; tctFile: begin TextConverterToolClasses.LoadCodeBufferFromFile(Self,FFilename, true,true,FCodeBuffer); ResetFile; end; end; end; end; FCurrentType:=AValue; end; procedure TIDETextConverter.SetFileIsTemporary(const AValue: boolean); begin if FFileIsTemporary=AValue then exit; FFileIsTemporary:=AValue; end; procedure TIDETextConverter.ConvertToFile(const NewFilename: string); var fs: TFileStream; TrimmedFilename: String; begin TrimmedFilename:=TrimFilename(NewFilename); case CurrentType of tctFile: if (FFilename<>'') and (FFilename<>TrimmedFilename) and (FileExistsUTF8(FFilename)) then RenameFileUTF8(FFilename,TrimmedFilename); tctSource: begin fs:=TFileStream.Create(TrimmedFilename,fmCreate); try if FSource<>'' then fs.Write(FSource[1],length(FSource)); finally fs.Free; end; end; tctStrings: begin FStrings.SaveToFile(TrimmedFilename); ResetStrings; end; tctCodeBuffer: begin TextConverterToolClasses.SaveCodeBufferToFile(Self,NewFilename); FCodeBuffer:=nil; end; end; FCurrentType:=tctFile; FFilename:=TrimmedFilename; end; procedure TIDETextConverter.SetCodeBuffer(const AValue: Pointer); begin CheckType(tctCodeBuffer); FCurrentType:=tctCodeBuffer; ResetStrings; FCodeBuffer:=AValue; end; procedure TIDETextConverter.CreateTempFilename; begin FFilename:=GetTempFilename; FFileIsTemporary:=true; end; function TIDETextConverter.GetCodeBuffer: Pointer; begin CurrentType:=tctCodeBuffer; Result:=FCodeBuffer; end; procedure TIDETextConverter.SetStringsIsTemporary(const AValue: Boolean); begin if FStringsIsTemporary=AValue then exit; FStringsIsTemporary:=AValue; end; function TIDETextConverter.GetTempFilename: string; begin if TextConverterToolClasses<>nil then Result:=TextConverterToolClasses.GetTempFilename else Result:=''; if Result='' then Result:='temp.txt'; end; constructor TIDETextConverter.Create(TheOwner: TComponent); begin inherited Create(TheOwner); FCurrentType:=tctSource; end; destructor TIDETextConverter.Destroy; begin ResetFile; ResetStrings; inherited Destroy; end; procedure TIDETextConverter.Clear; begin FFilename:=''; FSource:=''; FCodeBuffer:=nil; ResetStrings; FCurrentType:=tctSource; end; procedure TIDETextConverter.CheckType(aTextType: TTextConverterType); procedure RaiseNotSupported; begin raise Exception.Create('TIDETextConverter.CheckType:' +' type not supported '+GetEnumName(TypeInfo(TTextConverterType),ord(aTextType))); end; begin if not SupportsType(aTextType) then RaiseNotSupported; end; function TIDETextConverter.SupportsType(aTextType: TTextConverterType): boolean; begin Result:=(aTextType in [tctSource,tctFile,tctStrings]) or ((TextConverterToolClasses<>nil) and (TextConverterToolClasses.SupportsType(aTextType))); end; function TIDETextConverter.Execute(ToolList: TComponent; out ErrorTool: TComponent): TModalResult; var i: Integer; Tool: TCustomTextConverterTool; CurResult: TModalResult; begin Result:=mrOk; ErrorTool:=nil; for i:=0 to ToolList.ComponentCount-1 do begin if ToolList.Components[i] is TCustomTextConverterTool then begin Tool:=TCustomTextConverterTool(ToolList.Components[i]); if Tool.Enabled then begin Tool.ClearError; CurResult:=Tool.Execute(Self); if CurResult=mrIgnore then Result:=mrOk else if CurResult<>mrOk then begin ErrorTool:=Tool; exit(mrAbort); end; end; end; end; end; function TIDETextConverter.LoadFromFile(const AFilename: string; UseIDECache: Boolean; UpdateFromDisk: Boolean; Revert: Boolean): Boolean; var fs: TFileStream; begin if UseIDECache and (TextConverterToolClasses<>nil) then begin //DebugLn(['TIDETextConverter.LoadFromFile using IDE cache']); Result:=TextConverterToolClasses.LoadFromFile(Self,AFilename, UpdateFromDisk,Revert); end else begin //DebugLn(['TIDETextConverter.LoadFromFile loading directly CurrentType=',ord(CurrentType),' FFilename="',FFilename,'"']); Result:=false; try case CurrentType of tctSource: begin fs:=TFileStream.Create(AFilename,fmOpenRead); try SetLength(FSource,fs.Size); if fSource<>'' then fs.Read(fSource[1],length(fSource)); finally fs.Free; end; end; tctFile: CopyFile(AFilename,FFilename); tctStrings: FStrings.LoadFromFile(FFilename) end; Result:=true; except end; end; end; procedure TIDETextConverter.InitWithFilename(const AFilename: string); begin Clear; FCurrentType:=tctFile; FFilename:=AFilename; end; procedure TIDETextConverter.InitWithSource(const ASource: string); begin Clear; FCurrentType:=tctSource; FSource:=ASource; end; procedure TIDETextConverter.InitWithStrings(const aStrings: TStrings); begin Clear; FCurrentType:=tctStrings; FStrings:=aStrings; end; procedure TIDETextConverter.InitWithCodeBuffers(const aBuffer: Pointer); begin CheckType(tctCodeBuffer); Clear; FCurrentType:=tctCodeBuffer; FCodeBuffer:=aBuffer; end; { TCustomTextConverterTool } procedure TCustomTextConverterTool.SetCaption(const AValue: string); begin if FCaption=AValue then exit; FCaption:=AValue; end; function TCustomTextConverterTool.IsCaptionStored: boolean; begin Result:=Caption<>FirstLineOfClassDescription; end; procedure TCustomTextConverterTool.SetDescription(const AValue: string); begin if FDescription=AValue then exit; FDescription:=AValue; end; constructor TCustomTextConverterTool.Create(TheOwner: TComponent); begin inherited Create(TheOwner); Enabled:=true; Caption:=FirstLineOfClassDescription; end; procedure TCustomTextConverterTool.Assign(Source: TPersistent); var Src: TCustomTextConverterTool; begin if Source is TCustomTextConverterTool then begin Src:=TCustomTextConverterTool(Source); Caption:=Src.Caption; Description:=Src.Description; end else inherited Assign(Source); end; procedure TCustomTextConverterTool.ClearError; begin FErrorMsg:=''; FErrorLine:=0; FErrorColumn:=0; FErrorTopLine:=0; FErrorFilename:=''; end; procedure TCustomTextConverterTool.AssignError(Source: TCustomTextConverterTool); begin FErrorMsg:=Source.ErrorMsg; FErrorLine:=Source.ErrorLine; FErrorColumn:=Source.ErrorColumn; FErrorTopLine:=Source.ErrorTopLine; FErrorFilename:=Source.ErrorFilename; end; procedure TCustomTextConverterTool.AssignCodeToolBossError; begin if Assigned(TextConverterToolClasses) then TextConverterToolClasses.AssignCodeToolBossError(Self) else ClearError; end; class function TCustomTextConverterTool.FirstLineOfClassDescription: string; var p: Integer; begin Result:=ClassDescription; p:=1; while (p<=length(Result)) do begin if Result[p] in [#10,#13] then begin Result:=copy(Result,1,p-1); exit; end; inc(p); end; end; { TCustomTextReplaceTool } procedure TCustomTextReplaceTool.SetOptions(const AValue: TTextReplaceToolOptions); begin if FOptions=AValue then exit; FOptions:=AValue; end; procedure TCustomTextReplaceTool.SetReplaceWith(const AValue: string); begin if FReplaceWith=AValue then exit; FReplaceWith:=AValue; end; procedure TCustomTextReplaceTool.SetSearchFor(const AValue: string); begin if FSearchFor=AValue then exit; FSearchFor:=AValue; end; function TCustomTextReplaceTool.Execute(aText: TIDETextConverter): TModalResult; var Source: String; Flags: TSrcEditSearchOptions; Prompt: Boolean; begin //DebugLn(['TCustomTextReplaceTool.Execute ',dbgsName(Self),' aText=',dbgsName(aText),' SearchFor="',dbgstr(SearchFor),'"']); Result:=mrCancel; if aText=nil then exit; if SearchFor='' then exit(mrOk); Source:=aText.Source; Flags:=[sesoReplace,sesoReplaceAll]; if trtMatchCase in Options then Include(Flags,sesoMatchCase); if trtWholeWord in Options then Include(Flags,sesoWholeWord); if trtRegExpr in Options then Include(Flags,sesoRegExpr); if trtMultiLine in Options then Include(Flags,sesoMultiLine); Prompt:=false; Result:=IDESearchInText('',Source,SearchFor,ReplaceWith,Flags,Prompt,nil); if Result=mrOk then aText.Source:=Source; //DebugLn(['TCustomTextReplaceTool.Execute END Result=',Result=mrOk]); end; procedure TCustomTextReplaceTool.Assign(Source: TPersistent); var Src: TCustomTextReplaceTool; begin if Source is TCustomTextReplaceTool then begin Src:=TCustomTextReplaceTool(Source); SearchFor:=Src.SearchFor; ReplaceWith:=Src.ReplaceWith; Options:=Src.Options; end; inherited Assign(Source); end; class function TCustomTextReplaceTool.ClassDescription: string; begin Result := itcsSearchAndReplace; end; { TTextConverterToolClasses } function TTextConverterToolClasses.GetCount: integer; begin if Self<>nil then Result:=FItems.Count else Result:=0; end; function TTextConverterToolClasses.GetItems(Index: integer ): TCustomTextConverterToolClass; begin Result:=TCustomTextConverterToolClass(FItems[Index]); end; constructor TTextConverterToolClasses.Create; begin FItems:=TFPList.Create; end; destructor TTextConverterToolClasses.Destroy; begin FItems.Clear; FreeAndNil(FItems); inherited Destroy; end; procedure TTextConverterToolClasses.RegisterClass( AClass: TCustomTextConverterToolClass); begin if Self=nil then exit; if FItems.IndexOf(AClass)<0 then FItems.Add(AClass); end; procedure TTextConverterToolClasses.UnregisterClass( AClass: TCustomTextConverterToolClass); begin if Self=nil then exit; FItems.Remove(AClass); end; function TTextConverterToolClasses.FindByName(const aClassName: string ): TCustomTextConverterToolClass; var i: Integer; begin if Self<>nil then for i:=0 to FItems.Count-1 do begin Result:=Items[i]; if CompareText(Result.ClassName,aClassName)=0 then exit; end; Result:=nil; end; function TTextConverterToolClasses.FindByFirstLineOfClassDescription( const Line: string): TCustomTextConverterToolClass; var i: Integer; begin if Self<>nil then for i:=0 to FItems.Count-1 do begin Result:=Items[i]; if Result.FirstLineOfClassDescription=Line then exit; end; Result:=nil; end; procedure TTextConverterToolClasses.FindClass(Reader: TReader; const aClassName: string; var ComponentClass: TComponentClass); begin if Reader=nil then ; ComponentClass:=FindByName(aClassName); end; procedure TTextConverterToolClasses.AssignCodeToolBossError( Target: TCustomTextConverterTool); begin Target.ErrorMsg:=''; Target.ErrorLine:=-1; Target.ErrorColumn:=-1; Target.ErrorTopLine:=-1; Target.ErrorFilename:=''; end; initialization RegisterPropertyEditor(TypeInfo(AnsiString), TCustomTextReplaceTool, 'SearchFor', TStringMultilinePropertyEditor); RegisterPropertyEditor(TypeInfo(AnsiString), TCustomTextReplaceTool, 'ReplaceWith', TStringMultilinePropertyEditor); end.