280
303
LowKey:=LowerCase(Key);
281
304
// Form file resource rename or lowercase:
282
305
if (LowKey='dfm') or (LowKey='xfm') then begin
283
if fCTLink.Settings.Target in [ctLazarusDelphi, ctLazarusDelphiSameDfm] then begin
306
if Assigned(fCTLink.Settings) and fCTLink.Settings.SupportDelphi then begin
284
307
// Use the same dfm file. Lowercase existing key.
285
if (fCTLink.Settings.Target=ctLazarusDelphiSameDfm) and (Key<>LowKey) then
287
// Later IFDEF will be added so that Delphi can still use .dfm.
288
fDfmDirectiveStart:=ACleanPos;
289
fDfmDirectiveEnd:=ParamPos+6;
308
if fCTLink.Settings.SameDfmFile then begin
313
// Add IFDEF for .lfm and .dfm allowing Delphi to use .dfm.
314
s:='{$IFNDEF FPC}'+LineEnding+
315
' {$R *.dfm}'+LineEnding+
316
'{$ELSE}'+LineEnding+
317
' {$R *.lfm}'+LineEnding+
319
Result:=fCTLink.SrcCache.Replace(gtNone,gtNone,ACleanPos,ParamPos+6,s);
291
322
else // Change .dfm to .lfm.
294
// If there already is .lfm, prevent adding IFDEF for .dfm / .lfm.
295
else if LowKey='lfm' then
297
325
// lowercase {$R *.RES} to {$R *.res}
298
326
else if (Key='RES') and fLowerCaseRes then
302
if not fCTLink.SrcCache.Replace(gtNone,gtNone,ParamPos+2,ParamPos+5,NewKey) then exit;
328
// Change a single resource name.
329
if NewKey<>'' then begin
330
if not fCTLink.SrcCache.Replace(gtNone,gtNone,ParamPos+2,ParamPos+5,NewKey) then
304
334
ACleanPos:=FindCommentEnd(Src, ACleanPos, Scanner.NestedComments);
306
// if there is already .lfm file, don't add IFDEF for .dfm / .lfm.
307
if (fCTLink.Settings.Target=ctLazarusDelphi) and (fDfmDirectiveStart<>-1)
308
and not AlreadyIsLfm then begin
309
// Add IFDEF for .lfm and .dfm allowing Delphi to use .dfm.
310
s:='{$IFNDEF FPC}'+LineEnding+
311
' {$R *.dfm}'+LineEnding+
312
'{$ELSE}'+LineEnding+
313
' {$R *.lfm}'+LineEnding+
315
Result:=fCTLink.SrcCache.Replace(gtNone,gtNone,fDfmDirectiveStart,fDfmDirectiveEnd,s);
360
procedure SplitParam(const aStr: string; aDelimiter: Char; ResultList: TStringList);
361
// A modified split function. Removes '$' in front of every token.
363
procedure SetItem(Start, Len: integer); // Add the item.
365
while (aStr[Start]=' ') do begin // Trim leading space.
369
while (aStr[Start+Len-1]=' ') do // Trim trailing space.
371
if (aStr[Start]='$') then begin // Parameters must begin with '$'.
376
raise EDelphiConverterError.Create('Replacement function parameter should start with "$".');
377
ResultList.Add(Copy(aStr, Start, Len));
381
i, Start, EndPlus1: Integer;
387
while (i<Length(aStr)) and (aStr[i]<>aDelimiter) do
388
Inc(i); // Next delimiter.
390
if i<Length(aStr) then
392
SetItem(Start, EndPlus1-Start);
393
Start:=i+1; // Start of next item.
397
if EndPlus1>=Start then
398
SetItem(Start, EndPlus1-Start); // Copy the rest to last item.
399
Break; // Out of the loop.
404
381
function TConvDelphiCodeTool.ReplaceFuncsInSource: boolean;
405
382
// Replace the function names and parameters in source.
407
// Replacement parameter positions, will be converted to integers.
408
ParamList: TStringList;
409
BodyEnd: Integer; // End of function body.
384
ReplacementParams: TObjectList; // Replacement parameters.
411
386
function ParseReplacementParams(const aStr: string): integer;
412
387
// Parse replacement params. They show which original params are copied where.
413
388
// Returns the first position where comments can be searched from.
415
ParamBeg, ParamEnd: Integer; // Start and end of parameters.
390
i, xNum, xStart, xLen: Integer;
419
ParamBeg:=Pos('(', aStr);
420
if ParamBeg>0 then begin
421
ParamEnd:=PosEx(')', aStr, ParamBeg+1);
423
raise EDelphiConverterError.Create('")" is missing from replacement function.');
424
s:=Copy(aStr, ParamBeg+1, ParamEnd-ParamBeg-1);
425
SplitParam(s, ',', ParamList); // The actual parameter list.
393
while i<Length(aStr) do begin
395
if aStr[i]='$' then begin
398
while (i<Length(aStr)) and (aStr[i] in ['0'..'9']) do
399
Inc(i); // Get the number after '$'
402
raise EDelphiConverterError.Create('"$" should be followed by a number.');
403
xNum:=StrToInt(copy(aStr, xStart+1, xLen-1)); // Leave out '$', convert number.
405
raise EDelphiConverterError.Create(
406
'Replacement function parameter number should be >= 1.');
407
ReplacementParams.Add(TReplacementParam.Create(xNum, xLen, xStart));
411
raise EDelphiConverterError.Create('")" is missing from replacement function.');
431
function CollectParams(aParams: TStringList): string;
432
// Collect parameters from original call. Construct and return a new parameter list.
433
// aParams - parameters from the original function call.
415
function InsertParams2Replacement(FuncInfo: TFuncReplacement): string;
416
// Construct a new funcion call, inserting original parameters to replacement str.
417
// FuncInfo - Replacement string + parameters from the original function call.
436
ParamPos: Integer; // Position of parameter in the original call.
419
RP: TReplacementParam;
440
for i:=0 to ParamList.Count-1 do begin
441
ParamPos:=StrToInt(ParamList[i]);
443
raise EDelphiConverterError.Create('Replacement function parameter number should be >= 1.');
444
Param:='nil'; // Default value if not found from original code.
445
if ParamPos<=aParams.Count then
446
Param:=aParams[ParamPos-1];
449
Result:=Result+Param;
423
Result:=FuncInfo.ReplFunc;
424
for i:=ReplacementParams.Count-1 downto 0 do begin
425
RP:=TReplacementParam(ReplacementParams[i]);
426
if RP.ParamNum<=FuncInfo.Params.Count then begin
427
ss:=copy(Result, 1, RP.StrPosition-1); // String before the param
428
se:=copy(Result, RP.StrPosition+RP.ParamLen, MaxInt); // and after it.
429
Result:=ss+FuncInfo.Params[RP.ParamNum-1]+se;
464
445
CommChBeg:=PosEx('{', aStr, aPossibleStartPos);
465
446
if CommChBeg<>0 then begin
466
CommBeg:=CommChBeg+1;
447
CommBeg:=CommChBeg+1;
467
448
i:=PosEx('}', aStr, CommBeg);
472
if CommChBeg<>0 then begin
474
BodyEnd:=CommChBeg-1;
475
454
Result:=Trim(Copy(aStr, CommBeg, CommEnd-CommBeg+1));
480
458
FuncInfo: TFuncReplacement;
481
459
PossibleCommentPos: Integer; // Start looking for comments here.
483
s, NewFunc, NewParamStr, Comment: String;
461
s, NewFunc, Comment: String;
486
ParamList:=TStringList.Create;
464
ReplacementParams:=TObjectList.Create;
488
466
// Replace from bottom to top.
489
467
for i:=fFuncsToReplace.Count-1 downto 0 do begin
490
468
FuncInfo:=TFuncReplacement(fFuncsToReplace[i]);
469
// Update ReplacementParams.
470
ReplacementParams.Clear;
493
471
PossibleCommentPos:=ParseReplacementParams(FuncInfo.ReplFunc);
494
472
// Replace only if the params match somehow, so eg. a variable is not replaced.
495
if (FuncInfo.Params.Count>0) or (ParamList.Count=0) then begin
496
NewParamStr:=CollectParams(FuncInfo.Params);
473
if (FuncInfo.Params.Count>0) or (ReplacementParams.Count=0) then begin
474
NewFunc:=InsertParams2Replacement(FuncInfo);
497
475
Comment:=GetComment(FuncInfo.ReplFunc, PossibleCommentPos);
498
476
// Separate function body
500
BodyEnd:=Length(FuncInfo.ReplFunc);
501
NewFunc:=Trim(Copy(FuncInfo.ReplFunc, 1, BodyEnd));
502
NewFunc:=Format('%s(%s)%s { *Converted from %s* %s }',
503
[NewFunc, NewParamStr, FuncInfo.InclSemiColon, FuncInfo.FuncName, Comment]);
477
NewFunc:=Format('%s%s { *Converted from %s* %s }',
478
[NewFunc, FuncInfo.InclSemiColon, FuncInfo.FuncName, Comment]);
504
479
// Old function call with params for IDE message output.
505
480
s:=copy(fCTLink.CodeTool.Src, FuncInfo.StartPos, FuncInfo.EndPos-FuncInfo.StartPos);
506
s:=StringReplace(s, sLineBreak, '', [rfReplaceAll]);
481
s:=StringReplace(s, LineEnding, '', [rfReplaceAll]);
507
482
// Now replace it.
508
483
fCTLink.ResetMainScanner;
509
484
if not fCTLink.SrcCache.Replace(gtNone, gtNone,
510
485
FuncInfo.StartPos, FuncInfo.EndPos, NewFunc) then exit;
511
486
IDEMessagesWindow.AddMsg('Replaced call '+s, '', -1);
512
487
IDEMessagesWindow.AddMsg(' with '+NewFunc, '', -1);
488
// Add the required unit name to uses section if needed.
489
if Assigned(AddUnitEvent) and (FuncInfo.UnitName<>'') then
490
AddUnitEvent(FuncInfo.UnitName);
494
ReplacementParams.Free;
769
749
end; // ReplaceFuncCalls
771
function TConvDelphiCodeTool.CheckTopOffsets(LFMBuf: TCodeBuffer; LFMTree: TLFMTree;
772
VisOffsets: TVisualOffsets; ValueNodes: TObjectList): boolean;
773
// Collect a list of coord attributes for components that are inside
774
// a visual container component. An offset will be added to those attributes.
775
// Parameters: VisOffsets has names of parent container types.
776
// ValueNodes - the found coord attributes are added here as TSrcPropOffset objects.
777
// Based on function CheckLFM.
779
RootContext: TFindContext;
781
function CheckLFMObjectValues(LFMObject: TLFMObjectNode;
782
const ClassContext: TFindContext; GrandClassName: string): boolean; forward;
784
function FindLFMIdentifier(LFMNode: TLFMTreeNode; const IdentName: string;
785
const ClassContext: TFindContext; out IdentContext: TFindContext): boolean;
787
Params: TFindDeclarationParams;
788
IsPublished: Boolean;
791
IdentContext:=CleanFindContext;
793
if (ClassContext.Node=nil) or (not (ClassContext.Node.Desc in AllClasses)) then
795
Params:=TFindDeclarationParams.Create;
797
Params.Flags:=[fdfSearchInAncestors,fdfExceptionOnNotFound,
798
fdfExceptionOnPredefinedIdent,fdfIgnoreMissingParams,
799
fdfIgnoreOverloadedProcs];
800
Params.ContextNode:=ClassContext.Node;
801
Params.SetIdentifier(ClassContext.Tool,PChar(Pointer(IdentName)),nil);
803
if ClassContext.Tool.FindIdentifierInContext(Params) then begin
806
IdentContext:=CreateFindContext(Params);
808
and (IdentContext.Node.HasParentOfType(ctnClassPublished)) then
810
if (IdentContext.Node<>nil)
811
and (IdentContext.Node.Desc=ctnProperty)
812
and (IdentContext.Tool.PropNodeIsTypeLess(IdentContext.Node)) then
814
// this is a typeless property -> search further
816
Params.Flags:=[fdfSearchInAncestors, fdfIgnoreMissingParams,
817
fdfIgnoreCurContextNode, fdfIgnoreOverloadedProcs];
818
Params.ContextNode:=IdentContext.Node.Parent;
819
while (Params.ContextNode<>nil)
820
and (not (Params.ContextNode.Desc in AllClasses)) do
821
Params.ContextNode:=Params.ContextNode.Parent;
822
if Params.ContextNode<>nil then begin
823
Params.SetIdentifier(ClassContext.Tool,PChar(Pointer(IdentName)),nil);
824
if not IdentContext.Tool.FindIdentifierInContext(Params) then
832
on E: ECodeToolError do ; // ignore search/parse errors
839
function FindClassNodeForLFMObject(LFMNode: TLFMTreeNode;
840
StartTool: TFindDeclarationTool; DefinitionNode: TCodeTreeNode): TFindContext;
842
Params: TFindDeclarationParams;
844
OldInput: TFindDeclarationInput;
846
Result:=CleanFindContext;
847
if (DefinitionNode.Desc=ctnIdentifier) then
848
Identifier:=@StartTool.Src[DefinitionNode.StartPos]
849
else if DefinitionNode.Desc=ctnProperty then
850
Identifier:=StartTool.GetPropertyTypeIdentifier(DefinitionNode)
853
if Identifier=nil then exit;
854
Params:=TFindDeclarationParams.Create;
856
Params.Flags:=[fdfSearchInAncestors, fdfExceptionOnNotFound,
857
fdfSearchInParentNodes, fdfExceptionOnPredefinedIdent,
858
fdfIgnoreMissingParams, fdfIgnoreOverloadedProcs];
859
Params.ContextNode:=DefinitionNode;
860
Params.SetIdentifier(StartTool,Identifier,nil);
862
Params.Save(OldInput);
863
if StartTool.FindIdentifierInContext(Params) then begin
864
Params.Load(OldInput,true);
865
Result:=Params.NewCodeTool.FindBaseTypeOfNode(Params,Params.NewNode);
867
or (not (Result.Node.Desc in AllClasses)) then
868
Result:=CleanFindContext;
871
on E: ECodeToolError do ; // ignore search/parse errors
878
function FindClassContext(const ClassName: string): TFindContext;
880
Params: TFindDeclarationParams;
882
OldInput: TFindDeclarationInput;
883
StartTool: TStandardCodeTool;
885
Result:=CleanFindContext;
886
Params:=TFindDeclarationParams.Create;
887
StartTool:=fCTLink.CodeTool;
888
Identifier:=PChar(Pointer(ClassName));
890
Params.Flags:=[fdfExceptionOnNotFound, fdfSearchInParentNodes,
891
fdfExceptionOnPredefinedIdent,fdfIgnoreMissingParams,
892
fdfIgnoreOverloadedProcs];
893
with fCTLink.CodeTool do begin
894
Params.ContextNode:=FindInterfaceNode;
895
if Params.ContextNode=nil then
896
Params.ContextNode:=FindMainUsesSection;
897
Params.SetIdentifier(StartTool,Identifier,nil);
899
Params.Save(OldInput);
900
if FindIdentifierInContext(Params) then begin
901
Params.Load(OldInput,true);
902
Result:=Params.NewCodeTool.FindBaseTypeOfNode(Params,Params.NewNode);
904
or (not (Result.Node.Desc in AllClasses)) then
905
Result:=CleanFindContext;
908
on E: ECodeToolError do ; // ignore search/parse errors
916
procedure CheckLFMChildObject(LFMObject: TLFMObjectNode; const ParentName: string);
919
ChildContext: TFindContext;
920
ClassContext: TFindContext;
921
DefinitionNode: TCodeTreeNode;
923
// find variable for object
924
if LFMObject.Name='' then exit;
925
if FindLFMIdentifier(LFMObject, LFMObject.Name, RootContext, ChildContext) then begin
926
if ChildContext.Node=nil then exit;
927
// check if identifier is a variable or property
929
if (ChildContext.Node.Desc=ctnVarDefinition) then begin
930
DefinitionNode:=ChildContext.Tool.FindTypeNodeOfDefinition(ChildContext.Node);
931
if DefinitionNode=nil then exit;
932
VarTypeName:=ChildContext.Tool.ExtractDefinitionNodeType(ChildContext.Node);
933
end else if (ChildContext.Node.Desc=ctnProperty) then begin
934
DefinitionNode:=ChildContext.Node;
935
VarTypeName:=ChildContext.Tool.ExtractPropType(ChildContext.Node,false,false);
938
// check if variable/property has a compatible type
939
if (VarTypeName<>'') and (LFMObject.TypeName<>'')
940
and (CompareIdentifiers(PChar(VarTypeName),
941
PChar(LFMObject.TypeName))<>0) then exit;
943
ClassContext:=FindClassNodeForLFMObject(LFMObject, ChildContext.Tool, DefinitionNode);
945
ClassContext:=FindClassContext(LFMObject.TypeName); // try the object type
946
// check child LFM nodes
947
// ClassContext.Node=nil when the parent class is not found in source.
948
if ClassContext.Node<>nil then
949
CheckLFMObjectValues(LFMObject, ClassContext, ParentName);
952
function FindClassNodeForPropertyType(LFMProperty: TLFMPropertyNode;
953
const PropertyContext: TFindContext): TFindContext;
955
Params: TFindDeclarationParams;
957
Result:=CleanFindContext;
958
Params:=TFindDeclarationParams.Create;
960
Params.Flags:=[fdfSearchInAncestors, fdfExceptionOnNotFound,
961
fdfSearchInParentNodes,fdfExceptionOnPredefinedIdent,
962
fdfIgnoreMissingParams,fdfIgnoreOverloadedProcs];
963
Params.ContextNode:=PropertyContext.Node;
964
Params.SetIdentifier(PropertyContext.Tool,nil,nil);
966
Result:=PropertyContext.Tool.FindBaseTypeOfNode(Params, PropertyContext.Node);
968
on E: ECodeToolError do ; // ignore search/parse errors
975
procedure CheckLFMProperty(LFMProperty: TLFMPropertyNode; const ParentContext: TFindContext;
976
const GrandClassName, ParentClassName: string);
977
// Check properties. Stores info about Top and Left properties for later adjustment.
978
// Parameters: LFMProperty is the property node
979
// ParentContext is the context, where properties are searched (class or property).
980
// GrandClassName and ParentClassName are the class type names.
983
ValNode: TLFMValueNode;
984
CurName, Prop: string;
985
CurPropContext: TFindContext;
986
SearchContext: TFindContext;
988
// find complete property name
989
Prop:=LFMProperty.CompleteName;
990
if Prop='' then exit;
991
if (Prop='Top') or (Prop='Left') then begin
992
if (GrandClassName<>'') and VisOffsets.Find(GrandClassName, ind) then begin
993
if LFMProperty.FirstChild is TLFMValueNode then begin
994
ValNode:=LFMProperty.FirstChild as TLFMValueNode;
995
ValueNodes.Add(TSrcPropOffset.Create(GrandClassName,ParentClassName,
996
Prop,ValNode.StartPos));
1000
// find every part of the property name
1001
SearchContext:=ParentContext;
1002
for i:=0 to LFMProperty.NameParts.Count-1 do begin
1003
if SearchContext.Node.Desc=ctnProperty then begin
1004
// get the type of the property and search the class node
1005
SearchContext:=FindClassNodeForPropertyType(LFMProperty, SearchContext);
1006
if SearchContext.Node=nil then exit;
1008
CurName:=LFMProperty.NameParts.Names[i];
1009
if not FindLFMIdentifier(LFMProperty, CurName, SearchContext, CurPropContext) then
1011
if CurPropContext.Node=nil then break;
1012
SearchContext:=CurPropContext;
1016
function CheckLFMObjectValues(LFMObject: TLFMObjectNode;
1017
const ClassContext: TFindContext; GrandClassName: string): boolean;
1019
CurLFMNode: TLFMTreeNode;
1022
ParentName:=ClassContext.Tool.ExtractClassName(ClassContext.Node, False);
1023
CurLFMNode:=LFMObject.FirstChild;
1024
while CurLFMNode<>nil do begin
1025
case CurLFMNode.TheType of
1027
CheckLFMChildObject(TLFMObjectNode(CurLFMNode), ParentName);
1029
CheckLFMProperty(TLFMPropertyNode(CurLFMNode), ClassContext,
1030
GrandClassName, ParentName);
1032
CurLFMNode:=CurLFMNode.NextSibling;
1037
function CheckLFMRoot(RootLFMNode: TLFMTreeNode): boolean;
1039
LookupRootLFMNode: TLFMObjectNode;
1040
LookupRootTypeName: String;
1041
RootClassNode: TCodeTreeNode;
1044
// get root object node
1045
if (RootLFMNode=nil) or (not (RootLFMNode is TLFMObjectNode)) then exit;
1046
LookupRootLFMNode:=TLFMObjectNode(RootLFMNode);
1048
// get type name of root object
1049
LookupRootTypeName:=UpperCaseStr(LookupRootLFMNode.TypeName);
1050
if LookupRootTypeName='' then exit;
1053
RootClassNode:=fCTLink.CodeTool.FindClassNodeInUnit(LookupRootTypeName,
1054
true,false,false,false);
1055
RootContext:=CleanFindContext;
1056
RootContext.Node:=RootClassNode;
1057
RootContext.Tool:=fCTLink.CodeTool;
1058
if RootClassNode=nil then exit;
1059
Result:=CheckLFMObjectValues(LookupRootLFMNode, RootContext, '');
1063
CurRootLFMNode: TLFMTreeNode;
1066
// create tree from LFM file
1067
LFMTree:=DefaultLFMTrees.GetLFMTree(LFMBuf,true);
1068
fCTLink.CodeTool.ActivateGlobalWriteLock;
1070
if not LFMTree.ParseIfNeeded then exit;
1071
// parse unit and find LookupRoot
1072
fCTLink.CodeTool.BuildTree(true);
1073
// find every identifier
1074
CurRootLFMNode:=LFMTree.Root;
1075
while CurRootLFMNode<>nil do begin
1076
if not CheckLFMRoot(CurRootLFMNode) then exit;
1077
CurRootLFMNode:=CurRootLFMNode.NextSibling;
1080
fCTLink.CodeTool.DeactivateGlobalWriteLock;
1082
Result:=LFMTree.FirstError=nil;
1083
end; // CheckTopOffsets