1 {
2  *****************************************************************************
3   See the file COPYING.modifiedLGPL.txt, included in this distribution,
4   for details about the license.
5  *****************************************************************************
6 
7   Author: Mattias Gaertner
8 
9   Abstract:
10     Defines a converter and tools to modify a Text. The Text can be a file,
11     a string or a TStrings.
12     Packages can register extra tools, which the IDE user can then be put
13     together to define a series of changes. For example several Find&Replace
14     tools can be added and then executed automatically.
15     For an extensive example, see the package in components/h2pas/.
16 }
17 unit IDETextConverter;
18 
19 {$mode objfpc}{$H+}
20 
21 interface
22 
23 uses
24   Classes, SysUtils, TypInfo,
25   // LCL
26   LCLProc,
27   // LazUtils
28   UITypes, FileUtil, LazFileUtils, LazUTF8, LazLoggerBase,
29   // IdeIntf
30   SrcEditorIntf, PropEdits, ObjInspStrConsts;
31 
32 type
33   TCustomTextConverterTool = class;
34 
35   TTextConverterType = (
36     tctSource,
37     tctFile,
38     tctStrings,
39     tctCodeBuffer  // TCodeBuffer
40     );
41 
42   { TIDETextConverter
43     A component to hold a Text and tools to change the Text.
44     For example to do several find and replace operations on the text.
45     The Text can be a file, a string, TStrings or a TCodeBuffer.
46     The Text is converted on the fly, whenever someone reads/write one of the
47     formats.
48     The tools are decendants of TCustomTextConverterTool. }
49 
50   TIDETextConverter = class(TComponent)
51   private
52     FFilename: string;
53     FSource: string;
54     FCodeBuffer: Pointer;
55     FStrings: TStrings;
56     FCurrentType: TTextConverterType;
57     FFileIsTemporary: boolean;
58     FStringsIsTemporary: Boolean;
59     procedure CreateTempFilename;
GetCodeBuffernull60     function GetCodeBuffer: Pointer;
GetFilenamenull61     function GetFilename: string;
GetSourcenull62     function GetSource: string;
GetStringsnull63     function GetStrings: TStrings;
64     procedure ResetStrings;
65     procedure ResetFile;
66     procedure ConvertToFile(const NewFilename: string);
67     procedure SetCodeBuffer(const AValue: Pointer);
68     procedure SetFilename(const AValue: string);
69     procedure SetSource(const AValue: string);
70     procedure SetStrings(const AValue: TStrings);
71     procedure SetCurrentType(const AValue: TTextConverterType);
72     procedure SetFileIsTemporary(const AValue: boolean);
73     procedure SetStringsIsTemporary(const AValue: Boolean);
74   protected
GetTempFilenamenull75     function GetTempFilename: string; virtual;
76   public
77     constructor Create(TheOwner: TComponent); override;
78     destructor Destroy; override;
79     procedure Clear;
80     procedure CheckType(aTextType: TTextConverterType);
SupportsTypenull81     function SupportsType(aTextType: TTextConverterType): boolean; virtual;
Executenull82     function Execute(ToolList: TComponent; out ErrorTool: TComponent): TModalResult;// run the tools
LoadFromFilenull83     function LoadFromFile(const AFilename: string;
84                           UseIDECache: Boolean = true;
85                           UpdateFromDisk: Boolean = true;
86                           Revert: Boolean = false
87                           ): Boolean; virtual;
88     procedure InitWithFilename(const AFilename: string);
89     procedure InitWithSource(const ASource: string);
90     procedure InitWithStrings(const aStrings: TStrings);
91     procedure InitWithCodeBuffers(const aBuffer: Pointer);
92     property CurrentType: TTextConverterType read FCurrentType write SetCurrentType;
93     property Source: string read GetSource write SetSource;
94     property CodeBuffer: Pointer read GetCodeBuffer write SetCodeBuffer;
95     property Filename: string read GetFilename write SetFilename;
96     property Strings: TStrings read GetStrings write SetStrings;
97     property FileIsTemporary: boolean read FFileIsTemporary write SetFileIsTemporary;
98     property StringsIsTemporary: Boolean read FStringsIsTemporary write SetStringsIsTemporary;
99   end;
100 
101   { TCustomTextConverterTool
102     An abstract component to change a Text (TIDETextConverter). }
103 
104   TCustomTextConverterTool = class(TComponent)
105   private
106     FCaption: string;
107     FDescription: string;
108     FEnabled: boolean;
109     FErrorColumn: integer;
110     FErrorFilename: string;
111     FErrorLine: integer;
112     FErrorMsg: string;
113     FErrorTopLine: integer;
IsCaptionStorednull114     function IsCaptionStored: boolean;
115     procedure SetCaption(const AValue: string);
116     procedure SetDescription(const AValue: string);
117   public
ClassDescriptionnull118     class function ClassDescription: string; virtual; abstract;//the first line should be a short title
FirstLineOfClassDescriptionnull119     class function FirstLineOfClassDescription: string;
120     constructor Create(TheOwner: TComponent); override;
Executenull121     function Execute(aText: TIDETextConverter): TModalResult; virtual; abstract;
122     procedure Assign(Source: TPersistent); override;
123     procedure ClearError; virtual;
124     procedure AssignError(Source: TCustomTextConverterTool);
125     procedure AssignCodeToolBossError;
126     property ErrorMsg: string read FErrorMsg write FErrorMsg;
127     property ErrorLine: integer read FErrorLine write FErrorLine;
128     property ErrorColumn: integer read FErrorColumn write FErrorColumn;
129     property ErrorTopLine: integer read FErrorTopLine write FErrorTopLine;
130     property ErrorFilename: string read FErrorFilename write FErrorFilename;
131   published
132     property Caption: string read FCaption write SetCaption stored IsCaptionStored;
133     property Description: string read FDescription write SetDescription;
134     property Enabled: boolean read FEnabled write FEnabled default True;
135   end;
136   TCustomTextConverterToolClass = class of TCustomTextConverterTool;
137 
138   { TCustomTextReplaceTool
139     A tool to do a 'find and replace' in a text. }
140 
141   TTextReplaceToolOption = (
142     trtMatchCase,       // search case sensitive
143     trtWholeWord,       // search at word boundaries
144     trtRegExpr,         // use regular expressions for find and replace
145     trtMultiLine        // ignore type of line endings in pattern (e.g. #10 = #13#10)
146     //TODO trtSearchInReplacement,// when replaced, continue search at start of replacement, instead of end of replacement
147     //TODO trtReplaceUntilNotFound// restart replace until pattern not found
148     );
149   TTextReplaceToolOptions = set of TTextReplaceToolOption;
150 
151   TCustomTextReplaceTool = class(TCustomTextConverterTool)
152   private
153     FOptions: TTextReplaceToolOptions;
154     FReplaceWith: string;
155     FSearchFor: string;
156     procedure SetOptions(const AValue: TTextReplaceToolOptions);
157     procedure SetReplaceWith(const AValue: string);
158     procedure SetSearchFor(const AValue: string);
159   public
ClassDescriptionnull160     class function ClassDescription: string; override;
Executenull161     function Execute(aText: TIDETextConverter): TModalResult; override;
162     procedure Assign(Source: TPersistent); override;
163     property SearchFor: string read FSearchFor write SetSearchFor;
164     property ReplaceWith: string read FReplaceWith write SetReplaceWith;
165     property Options: TTextReplaceToolOptions read FOptions write SetOptions;
166   end;
167 
168   { TTextReplaceTool }
169 
170   TTextReplaceTool = class(TCustomTextReplaceTool)
171   published
172     property SearchFor;
173     property ReplaceWith;
174     property Options;
175   end;
176 
177   { TTextConverterToolClasses
178     A list to hold the registered TCustomTextConverterToolClass(es) }
179 
180   TTextConverterToolClasses = class
181   private
182     FItems: TFPList;// list of TCustomTextConverterToolClass
GetCountnull183     function GetCount: integer;
GetItemsnull184     function GetItems(Index: integer): TCustomTextConverterToolClass;
185   public
186     constructor Create;
187     destructor Destroy; override;
188     procedure RegisterClass(AClass: TCustomTextConverterToolClass);
189     procedure UnregisterClass(AClass: TCustomTextConverterToolClass);
FindByNamenull190     function FindByName(const aClassName: string): TCustomTextConverterToolClass;
FindByFirstLineOfClassDescriptionnull191     function FindByFirstLineOfClassDescription(const Line: string
192                                                ): TCustomTextConverterToolClass;
193     procedure FindClass(Reader: TReader; const aClassName: string;
194                         var ComponentClass: TComponentClass);
195     property Items[Index: integer]: TCustomTextConverterToolClass read GetItems; default;
196     property Count: integer read GetCount;
197 
SupportsTypenull198     function SupportsType(aTextType: TTextConverterType): boolean; virtual; abstract;
GetTempFilenamenull199     function GetTempFilename: string; virtual; abstract;
LoadFromFilenull200     function LoadFromFile(Converter: TIDETextConverter; const AFilename: string;
201                           UpdateFromDisk, Revert: Boolean): Boolean; virtual; abstract;
SaveCodeBufferToFilenull202     function SaveCodeBufferToFile(Converter: TIDETextConverter;
203                            const AFilename: string): Boolean; virtual; abstract;
GetCodeBufferSourcenull204     function GetCodeBufferSource(Converter: TIDETextConverter;
205                                 out Source: string): boolean; virtual; abstract;
CreateCodeBuffernull206     function CreateCodeBuffer(Converter: TIDETextConverter;
207                               const Filename, NewSource: string;
208                               out CodeBuffer: Pointer): boolean; virtual; abstract;
LoadCodeBufferFromFilenull209     function LoadCodeBufferFromFile(Converter: TIDETextConverter;
210                                  const Filename: string;
211                                  UpdateFromDisk, Revert: Boolean;
212                                  out CodeBuffer: Pointer): boolean; virtual; abstract;
213     procedure AssignCodeToolBossError(Target: TCustomTextConverterTool); virtual;
214   end;
215 
216 var
217   TextConverterToolClasses: TTextConverterToolClasses = nil;// set by the IDE
218 
219 procedure ClearTextConverterToolList(List: TComponent);
220 procedure CopyTextConverterToolList(Src, Dest: TComponent;
221                                     ClearDest: boolean = true);
222 
223 procedure MakeToolNameUnique(List: TComponent;
224                              NewTool: TCustomTextConverterTool);
225 procedure MakeToolCaptionUnique(List: TComponent;
226                                 NewTool: TCustomTextConverterTool);
227 procedure MakeToolCaptionAndNameUnique(List: TComponent;
228                                        NewTool: TCustomTextConverterTool);
AddNewTextConverterToolnull229 function AddNewTextConverterTool(List: TComponent;
230              NewClass: TCustomTextConverterToolClass): TCustomTextConverterTool;
231 
232 
233 implementation
234 
235 
236 procedure MakeToolNameUnique(List: TComponent;
237   NewTool: TCustomTextConverterTool);
238 var
239   NewName: String;
240 
241   procedure MakeValidIdentifier;
242   var
243     i: Integer;
244   begin
245     for i:=length(NewName) downto 1 do
246       if not (NewName[i] in ['0'..'9','_','a'..'z','A'..'Z']) then
247         System.Delete(NewName,i,1);
248     if (NewName<>'') and (NewName[1] in ['0'..'9']) then
249       NewName:='_'+NewName;
250   end;
251 
NameIsUniquenull252   function NameIsUnique: Boolean;
253   var
254     i: Integer;
255     CurTool: TCustomTextConverterTool;
256   begin
257     MakeValidIdentifier;
258     if NewName='' then exit(false);
259     for i:=0 to List.ComponentCount-1 do begin
260       CurTool:=TCustomTextConverterTool(List.Components[i]);
261       if CurTool=NewTool then continue;
262       if CompareText(CurTool.Name,NewName)=0 then exit(false);
263     end;
264     Result:=true;
265     NewTool.Name:=NewName;
266   end;
267 
268 begin
269   NewName:=NewTool.Name;
270   if NameIsUnique then exit;
271   NewName:=NewTool.FirstLineOfClassDescription;
272   if NewName='' then NewName:=NewTool.ClassName;
273   while not NameIsUnique do
274     NewName:=CreateNextIdentifier(NewName);
275 end;
276 
277 procedure MakeToolCaptionUnique(List: TComponent;
278   NewTool: TCustomTextConverterTool);
279 var
280   NewCaption: String;
281 
CaptionIsUniquenull282   function CaptionIsUnique: Boolean;
283   var
284     i: Integer;
285     CurTool: TCustomTextConverterTool;
286   begin
287     if NewCaption='' then exit(false);
288     for i:=0 to List.ComponentCount-1 do begin
289       CurTool:=TCustomTextConverterTool(List.Components[i]);
290       if CurTool=NewTool then continue;
291       if CompareText(CurTool.Caption,NewCaption)=0 then exit(false);
292     end;
293     Result:=true;
294     NewTool.Caption:=NewCaption;
295   end;
296 
297 begin
298   NewCaption:=NewTool.Caption;
299   if CaptionIsUnique then exit;
300   NewCaption:=NewTool.FirstLineOfClassDescription;
301   if NewCaption='' then NewCaption:=NewTool.ClassName;
302   while not CaptionIsUnique do
303     NewCaption:=CreateNextIdentifier(NewCaption);
304 end;
305 
306 procedure MakeToolCaptionAndNameUnique(List: TComponent;
307   NewTool: TCustomTextConverterTool);
308 begin
309   MakeToolNameUnique(List,NewTool);
310   MakeToolCaptionUnique(List,NewTool);
311 end;
312 
AddNewTextConverterToolnull313 function AddNewTextConverterTool(List: TComponent;
314   NewClass: TCustomTextConverterToolClass): TCustomTextConverterTool;
315 begin
316   Result:=NewClass.Create(List);
317   MakeToolCaptionAndNameUnique(List,Result);
318   if Result.Caption='' then RaiseGDBException('');
319 end;
320 
321 procedure ClearTextConverterToolList(List: TComponent);
322 begin
323   if List=nil then exit;
324   while List.ComponentCount>0 do
325     List.Components[List.ComponentCount-1].Free;
326 end;
327 
328 procedure CopyTextConverterToolList(Src, Dest: TComponent; ClearDest: boolean);
329 var
330   i: Integer;
331   SrcTool: TCustomTextConverterTool;
332   NewTool: TCustomTextConverterTool;
333 begin
334   if ClearDest then
335     ClearTextConverterToolList(Dest);
336   for i:=0 to Src.ComponentCount-1 do begin
337     SrcTool:=Src.Components[i] as TCustomTextConverterTool;
338     NewTool:=TCustomTextConverterToolClass(SrcTool.ClassType).Create(Dest);
339     NewTool.Assign(SrcTool);
340     NewTool.Name:=SrcTool.Name;
341   end;
342 end;
343 
344 { TIDETextConverter }
345 
346 procedure TIDETextConverter.SetFilename(const AValue: string);
347 begin
348   ConvertToFile(AValue);
349 end;
350 
TIDETextConverter.GetFilenamenull351 function TIDETextConverter.GetFilename: string;
352 begin
353   CurrentType:=tctFile;
354   Result:=FFilename;
355 end;
356 
TIDETextConverter.GetSourcenull357 function TIDETextConverter.GetSource: string;
358 begin
359   CurrentType:=tctSource;
360   Result:=FSource;
361 end;
362 
TIDETextConverter.GetStringsnull363 function TIDETextConverter.GetStrings: TStrings;
364 begin
365   CurrentType:=tctStrings;
366   Result:=FStrings;
367 end;
368 
369 procedure TIDETextConverter.ResetStrings;
370 begin
371   if StringsIsTemporary then
372     FStrings.Free;
373   FStrings:=nil;
374   FStringsIsTemporary:=false;
375 end;
376 
377 procedure TIDETextConverter.ResetFile;
378 begin
379   if FileIsTemporary then begin
380     DeleteFileUTF8(FFilename);
381     // do not change FFileIsTemporary, so that File > Source > File sequences
382     // keep the file temporary.
383   end;
384 end;
385 
386 procedure TIDETextConverter.SetSource(const AValue: string);
387 begin
388   FCurrentType:=tctSource;
389   ResetStrings;
390   ResetFile;
391   FSource:=AValue;
392 end;
393 
394 procedure TIDETextConverter.SetStrings(const AValue: TStrings);
395 begin
396   FCurrentType:=tctStrings;
397   ResetFile;
398   ResetStrings;
399   FStrings:=AValue;
400 end;
401 
402 procedure TIDETextConverter.SetCurrentType(const AValue: TTextConverterType);
403 var
404   fs: TFileStream;
405 begin
406   if FCurrentType=AValue then exit;
407   CheckType(AValue);
408   //DebugLn(['TIDETextConverter.SetCurrentType ',ord(FCurrentType),' ',ord(AValue)]);
409   case AValue of
410   tctSource:
411     // convert to Source
412     begin
413       FSource:='';
414       case FCurrentType of
415       tctStrings:
416         if FStrings<>nil then begin
417           FSource:=FStrings.Text;
418           ResetStrings;
419         end;
420       tctFile:
421         if FileExistsUTF8(FFilename) then begin
422           fs:=TFileStream.Create(FFilename,fmOpenRead);
423           try
424             SetLength(FSource,fs.Size);
425             fs.Read(FSource[1],length(FSource));
426           finally
427             fs.Free;
428           end;
429           ResetFile;
430         end;
431       tctCodeBuffer:
432         begin
433           TextConverterToolClasses.GetCodeBufferSource(Self,FSource);
434           FCodeBuffer:=nil;
435         end;
436       end;
437     end;
438 
439   tctStrings:
440     // convert to TStrings
441     begin
442       if FStrings<>nil then
443         RaiseGDBException('TTextConverterText.SetCurrentType FStrings<>nil');
444       FStrings:=TStringList.Create;
445       fStringsIsTemporary:=true;
446       case FCurrentType of
447       tctSource:
448         begin
449           FStrings.Text:=FSource;
450           FSource:='';
451         end;
452       tctFile:
453         if FileExistsUTF8(FFilename) then begin
454           FStrings.LoadFromFile(FFilename);
455           ResetFile;
456         end;
457       tctCodeBuffer:
458         begin
459           TextConverterToolClasses.GetCodeBufferSource(Self,FSource);
460           FStrings.Text:=FSource;
461           FSource:='';
462           FCodeBuffer:=nil;
463         end;
464       end;
465     end;
466 
467   tctFile:
468     // convert to File
469     begin
470       // keep old Filename, so that a Filename, Source, Filename combination
471       // uses the same Filename
472       if FFilename='' then
473         CreateTempFilename;
474       case FCurrentType of
475       tctSource:
476         begin
477           fs:=TFileStream.Create(FFilename,fmCreate);
478           try
479             if FSource<>'' then begin
480               fs.Write(FSource[1],length(FSource));
481               FSource:='';
482             end;
483           finally
484             fs.Free;
485           end;
486         end;
487       tctStrings:
488         if FStrings<>nil then begin
489           FStrings.LoadFromFile(FFilename);
490           ResetStrings;
491         end;
492       tctCodeBuffer:
493         begin
494           TextConverterToolClasses.SaveCodeBufferToFile(Self,FFilename);
495           FCodeBuffer:=nil;
496         end;
497       end;
498     end;
499 
500   tctCodeBuffer:
501     // convert to CodeBuffer
502     begin
503       // keep old Filename, so that a Filename, Source, Filename combination
504       // uses the same Filename
505       if FFilename='' then
506         CreateTempFilename;
507       case FCurrentType of
508       tctSource:
509         begin
510           TextConverterToolClasses.CreateCodeBuffer(Self,FFilename,FSource,
511                                                     FCodeBuffer);
512           FSource:='';
513         end;
514       tctStrings:
515         begin
516           TextConverterToolClasses.CreateCodeBuffer(Self,FFilename,
517                                                     FStrings.Text,FCodeBuffer);
518           ResetStrings;
519         end;
520       tctFile:
521         begin
522           TextConverterToolClasses.LoadCodeBufferFromFile(Self,FFilename,
523                                                          true,true,FCodeBuffer);
524           ResetFile;
525         end;
526       end;
527     end;
528   end;
529   FCurrentType:=AValue;
530 end;
531 
532 procedure TIDETextConverter.SetFileIsTemporary(const AValue: boolean);
533 begin
534   if FFileIsTemporary=AValue then exit;
535   FFileIsTemporary:=AValue;
536 end;
537 
538 procedure TIDETextConverter.ConvertToFile(const NewFilename: string);
539 var
540   fs: TFileStream;
541   TrimmedFilename: String;
542 begin
543   TrimmedFilename:=TrimFilename(NewFilename);
544   case CurrentType of
545   tctFile:
546     if (FFilename<>'') and (FFilename<>TrimmedFilename)
547     and (FileExistsUTF8(FFilename)) then
548       RenameFileUTF8(FFilename,TrimmedFilename);
549   tctSource:
550     begin
551       fs:=TFileStream.Create(TrimmedFilename,fmCreate);
552       try
553         if FSource<>'' then
554           fs.Write(FSource[1],length(FSource));
555       finally
556         fs.Free;
557       end;
558     end;
559   tctStrings:
560     begin
561       FStrings.SaveToFile(TrimmedFilename);
562       ResetStrings;
563     end;
564   tctCodeBuffer:
565     begin
566       TextConverterToolClasses.SaveCodeBufferToFile(Self,NewFilename);
567       FCodeBuffer:=nil;
568     end;
569   end;
570   FCurrentType:=tctFile;
571   FFilename:=TrimmedFilename;
572 end;
573 
574 procedure TIDETextConverter.SetCodeBuffer(const AValue: Pointer);
575 begin
576   CheckType(tctCodeBuffer);
577   FCurrentType:=tctCodeBuffer;
578   ResetStrings;
579   FCodeBuffer:=AValue;
580 end;
581 
582 procedure TIDETextConverter.CreateTempFilename;
583 begin
584   FFilename:=GetTempFilename;
585   FFileIsTemporary:=true;
586 end;
587 
TIDETextConverter.GetCodeBuffernull588 function TIDETextConverter.GetCodeBuffer: Pointer;
589 begin
590   CurrentType:=tctCodeBuffer;
591   Result:=FCodeBuffer;
592 end;
593 
594 procedure TIDETextConverter.SetStringsIsTemporary(const AValue: Boolean);
595 begin
596   if FStringsIsTemporary=AValue then exit;
597   FStringsIsTemporary:=AValue;
598 end;
599 
GetTempFilenamenull600 function TIDETextConverter.GetTempFilename: string;
601 begin
602   if TextConverterToolClasses<>nil then
603     Result:=TextConverterToolClasses.GetTempFilename
604   else
605     Result:='';
606   if Result='' then
607     Result:='temp.txt';
608 end;
609 
610 constructor TIDETextConverter.Create(TheOwner: TComponent);
611 begin
612   inherited Create(TheOwner);
613   FCurrentType:=tctSource;
614 end;
615 
616 destructor TIDETextConverter.Destroy;
617 begin
618   ResetFile;
619   ResetStrings;
620   inherited Destroy;
621 end;
622 
623 procedure TIDETextConverter.Clear;
624 begin
625   FFilename:='';
626   FSource:='';
627   FCodeBuffer:=nil;
628   ResetStrings;
629   FCurrentType:=tctSource;
630 end;
631 
632 procedure TIDETextConverter.CheckType(aTextType: TTextConverterType);
633 
634   procedure RaiseNotSupported;
635   begin
636     raise Exception.Create('TIDETextConverter.CheckType:'
637       +' type not supported '+GetEnumName(TypeInfo(TTextConverterType),ord(aTextType)));
638   end;
639 
640 begin
641   if not SupportsType(aTextType) then RaiseNotSupported;
642 end;
643 
TIDETextConverter.SupportsTypenull644 function TIDETextConverter.SupportsType(aTextType: TTextConverterType): boolean;
645 begin
646   Result:=(aTextType in [tctSource,tctFile,tctStrings])
647       or ((TextConverterToolClasses<>nil)
648            and (TextConverterToolClasses.SupportsType(aTextType)));
649 end;
650 
TIDETextConverter.Executenull651 function TIDETextConverter.Execute(ToolList: TComponent;
652   out ErrorTool: TComponent): TModalResult;
653 var
654   i: Integer;
655   Tool: TCustomTextConverterTool;
656   CurResult: TModalResult;
657 begin
658   Result:=mrOk;
659   ErrorTool:=nil;
660   for i:=0 to ToolList.ComponentCount-1 do begin
661     if ToolList.Components[i] is TCustomTextConverterTool then begin
662       Tool:=TCustomTextConverterTool(ToolList.Components[i]);
663       if Tool.Enabled then begin
664         Tool.ClearError;
665         CurResult:=Tool.Execute(Self);
666         if CurResult=mrIgnore then
667           Result:=mrOk
668         else if CurResult<>mrOk then begin
669           ErrorTool:=Tool;
670           exit(mrAbort);
671         end;
672       end;
673     end;
674   end;
675 end;
676 
TIDETextConverter.LoadFromFilenull677 function TIDETextConverter.LoadFromFile(const AFilename: string;
678   UseIDECache: Boolean; UpdateFromDisk: Boolean; Revert: Boolean): Boolean;
679 var
680   fs: TFileStream;
681 begin
682   if UseIDECache and (TextConverterToolClasses<>nil) then begin
683     //DebugLn(['TIDETextConverter.LoadFromFile using IDE cache']);
684     Result:=TextConverterToolClasses.LoadFromFile(Self,AFilename,
685                                                   UpdateFromDisk,Revert);
686   end else begin
687     //DebugLn(['TIDETextConverter.LoadFromFile loading directly CurrentType=',ord(CurrentType),' FFilename="',FFilename,'"']);
688     Result:=false;
689     try
690       case CurrentType of
691       tctSource:
692         begin
693           fs:=TFileStream.Create(AFilename,fmOpenRead);
694           try
695             SetLength(FSource,fs.Size);
696             if fSource<>'' then
697               fs.Read(fSource[1],length(fSource));
698           finally
699             fs.Free;
700           end;
701         end;
702       tctFile:
703         CopyFile(AFilename,FFilename);
704       tctStrings:
705         FStrings.LoadFromFile(FFilename)
706       end;
707       Result:=true;
708     except
709     end;
710   end;
711 end;
712 
713 procedure TIDETextConverter.InitWithFilename(const AFilename: string);
714 begin
715   Clear;
716   FCurrentType:=tctFile;
717   FFilename:=AFilename;
718 end;
719 
720 procedure TIDETextConverter.InitWithSource(const ASource: string);
721 begin
722   Clear;
723   FCurrentType:=tctSource;
724   FSource:=ASource;
725 end;
726 
727 procedure TIDETextConverter.InitWithStrings(const aStrings: TStrings);
728 begin
729   Clear;
730   FCurrentType:=tctStrings;
731   FStrings:=aStrings;
732 end;
733 
734 procedure TIDETextConverter.InitWithCodeBuffers(const aBuffer: Pointer);
735 begin
736   CheckType(tctCodeBuffer);
737   Clear;
738   FCurrentType:=tctCodeBuffer;
739   FCodeBuffer:=aBuffer;
740 end;
741 
742 { TCustomTextConverterTool }
743 
744 procedure TCustomTextConverterTool.SetCaption(const AValue: string);
745 begin
746   if FCaption=AValue then exit;
747   FCaption:=AValue;
748 end;
749 
IsCaptionStorednull750 function TCustomTextConverterTool.IsCaptionStored: boolean;
751 begin
752   Result:=Caption<>FirstLineOfClassDescription;
753 end;
754 
755 procedure TCustomTextConverterTool.SetDescription(const AValue: string);
756 begin
757   if FDescription=AValue then exit;
758   FDescription:=AValue;
759 end;
760 
761 constructor TCustomTextConverterTool.Create(TheOwner: TComponent);
762 begin
763   inherited Create(TheOwner);
764   Enabled:=true;
765   Caption:=FirstLineOfClassDescription;
766 end;
767 
768 procedure TCustomTextConverterTool.Assign(Source: TPersistent);
769 var
770   Src: TCustomTextConverterTool;
771 begin
772   if Source is TCustomTextConverterTool then begin
773     Src:=TCustomTextConverterTool(Source);
774     Caption:=Src.Caption;
775     Description:=Src.Description;
776   end else
777     inherited Assign(Source);
778 end;
779 
780 procedure TCustomTextConverterTool.ClearError;
781 begin
782   FErrorMsg:='';
783   FErrorLine:=0;
784   FErrorColumn:=0;
785   FErrorTopLine:=0;
786   FErrorFilename:='';
787 end;
788 
789 procedure TCustomTextConverterTool.AssignError(Source: TCustomTextConverterTool);
790 begin
791   FErrorMsg:=Source.ErrorMsg;
792   FErrorLine:=Source.ErrorLine;
793   FErrorColumn:=Source.ErrorColumn;
794   FErrorTopLine:=Source.ErrorTopLine;
795   FErrorFilename:=Source.ErrorFilename;
796 end;
797 
798 procedure TCustomTextConverterTool.AssignCodeToolBossError;
799 begin
800   if Assigned(TextConverterToolClasses) then
801     TextConverterToolClasses.AssignCodeToolBossError(Self)
802   else
803     ClearError;
804 end;
805 
TCustomTextConverterTool.FirstLineOfClassDescriptionnull806 class function TCustomTextConverterTool.FirstLineOfClassDescription: string;
807 var
808   p: Integer;
809 begin
810   Result:=ClassDescription;
811   p:=1;
812   while (p<=length(Result)) do begin
813     if Result[p] in [#10,#13] then begin
814       Result:=copy(Result,1,p-1);
815       exit;
816     end;
817     inc(p);
818   end;
819 end;
820 
821 { TCustomTextReplaceTool }
822 
823 procedure TCustomTextReplaceTool.SetOptions(const AValue: TTextReplaceToolOptions);
824 begin
825   if FOptions=AValue then exit;
826   FOptions:=AValue;
827 end;
828 
829 procedure TCustomTextReplaceTool.SetReplaceWith(const AValue: string);
830 begin
831   if FReplaceWith=AValue then exit;
832   FReplaceWith:=AValue;
833 end;
834 
835 procedure TCustomTextReplaceTool.SetSearchFor(const AValue: string);
836 begin
837   if FSearchFor=AValue then exit;
838   FSearchFor:=AValue;
839 end;
840 
TCustomTextReplaceTool.Executenull841 function TCustomTextReplaceTool.Execute(aText: TIDETextConverter): TModalResult;
842 var
843   Source: String;
844   Flags: TSrcEditSearchOptions;
845   Prompt: Boolean;
846 begin
847   //DebugLn(['TCustomTextReplaceTool.Execute ',dbgsName(Self),' aText=',dbgsName(aText),' SearchFor="',dbgstr(SearchFor),'"']);
848   Result:=mrCancel;
849   if aText=nil then exit;
850   if SearchFor='' then exit(mrOk);
851   Source:=aText.Source;
852   Flags:=[sesoReplace,sesoReplaceAll];
853   if trtMatchCase in Options then Include(Flags,sesoMatchCase);
854   if trtWholeWord in Options then Include(Flags,sesoWholeWord);
855   if trtRegExpr in Options then Include(Flags,sesoRegExpr);
856   if trtMultiLine in Options then Include(Flags,sesoMultiLine);
857   Prompt:=false;
858   Result:=IDESearchInText('',Source,SearchFor,ReplaceWith,Flags,Prompt,nil);
859   if Result=mrOk then
860     aText.Source:=Source;
861   //DebugLn(['TCustomTextReplaceTool.Execute END Result=',Result=mrOk]);
862 end;
863 
864 procedure TCustomTextReplaceTool.Assign(Source: TPersistent);
865 var
866   Src: TCustomTextReplaceTool;
867 begin
868   if Source is TCustomTextReplaceTool then begin
869     Src:=TCustomTextReplaceTool(Source);
870     SearchFor:=Src.SearchFor;
871     ReplaceWith:=Src.ReplaceWith;
872     Options:=Src.Options;
873   end;
874   inherited Assign(Source);
875 end;
876 
TCustomTextReplaceTool.ClassDescriptionnull877 class function TCustomTextReplaceTool.ClassDescription: string;
878 begin
879   Result := itcsSearchAndReplace;
880 end;
881 
882 { TTextConverterToolClasses }
883 
TTextConverterToolClasses.GetCountnull884 function TTextConverterToolClasses.GetCount: integer;
885 begin
886   if Self<>nil then
887     Result:=FItems.Count
888   else
889     Result:=0;
890 end;
891 
TTextConverterToolClasses.GetItemsnull892 function TTextConverterToolClasses.GetItems(Index: integer
893   ): TCustomTextConverterToolClass;
894 begin
895   Result:=TCustomTextConverterToolClass(FItems[Index]);
896 end;
897 
898 constructor TTextConverterToolClasses.Create;
899 begin
900   FItems:=TFPList.Create;
901 end;
902 
903 destructor TTextConverterToolClasses.Destroy;
904 begin
905   FItems.Clear;
906   FreeAndNil(FItems);
907   inherited Destroy;
908 end;
909 
910 procedure TTextConverterToolClasses.RegisterClass(
911   AClass: TCustomTextConverterToolClass);
912 begin
913   if Self=nil then exit;
914   if FItems.IndexOf(AClass)<0 then
915     FItems.Add(AClass);
916 end;
917 
918 procedure TTextConverterToolClasses.UnregisterClass(
919   AClass: TCustomTextConverterToolClass);
920 begin
921   if Self=nil then exit;
922   FItems.Remove(AClass);
923 end;
924 
TTextConverterToolClasses.FindByNamenull925 function TTextConverterToolClasses.FindByName(const aClassName: string
926   ): TCustomTextConverterToolClass;
927 var
928   i: Integer;
929 begin
930   if Self<>nil then
931     for i:=0 to FItems.Count-1 do begin
932       Result:=Items[i];
933       if CompareText(Result.ClassName,aClassName)=0 then exit;
934     end;
935   Result:=nil;
936 end;
937 
FindByFirstLineOfClassDescriptionnull938 function TTextConverterToolClasses.FindByFirstLineOfClassDescription(
939   const Line: string): TCustomTextConverterToolClass;
940 var
941   i: Integer;
942 begin
943   if Self<>nil then
944     for i:=0 to FItems.Count-1 do begin
945       Result:=Items[i];
946       if Result.FirstLineOfClassDescription=Line then exit;
947     end;
948   Result:=nil;
949 end;
950 
951 procedure TTextConverterToolClasses.FindClass(Reader: TReader;
952   const aClassName: string; var ComponentClass: TComponentClass);
953 begin
954   if Reader=nil then ;
955   ComponentClass:=FindByName(aClassName);
956 end;
957 
958 procedure TTextConverterToolClasses.AssignCodeToolBossError(
959   Target: TCustomTextConverterTool);
960 begin
961   Target.ErrorMsg:='';
962   Target.ErrorLine:=-1;
963   Target.ErrorColumn:=-1;
964   Target.ErrorTopLine:=-1;
965   Target.ErrorFilename:='';
966 end;
967 
968 initialization
969   RegisterPropertyEditor(TypeInfo(AnsiString),
970     TCustomTextReplaceTool, 'SearchFor', TStringMultilinePropertyEditor);
971   RegisterPropertyEditor(TypeInfo(AnsiString),
972     TCustomTextReplaceTool, 'ReplaceWith', TStringMultilinePropertyEditor);
973 
974 end.
975 
976