mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-04-05 01:08:12 +02:00
766 lines
24 KiB
ObjectPascal
766 lines
24 KiB
ObjectPascal
{ $Id$ }
|
|
{
|
|
/***************************************************************************
|
|
MissingPropertiesDlg.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 <http://www.gnu.org/copyleft/gpl.html>. You can also *
|
|
* obtain it by writing to the Free Software Foundation, *
|
|
* Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
|
|
* *
|
|
***************************************************************************
|
|
}
|
|
unit MissingPropertiesDlg;
|
|
|
|
{$mode objfpc}{$H+}
|
|
|
|
interface
|
|
|
|
uses
|
|
// FCL+LCL
|
|
Classes, SysUtils, Contnrs,
|
|
Forms, Controls, Grids, LResources, Dialogs, Buttons, StdCtrls, ExtCtrls,
|
|
// LazUtils
|
|
LazFileUtils, LazUTF8, LazLoggerBase, AvgLvlTree,
|
|
// components
|
|
SynHighlighterLFM, SynEdit, SynEditMiscClasses,
|
|
// codetools
|
|
CodeCache, CodeToolManager, CodeCompletionTool, LFMTrees,
|
|
// BuildIntf
|
|
ComponentReg,
|
|
// IdeIntf
|
|
IDEExternToolIntf, IDEImagesIntf,
|
|
// IdeUtils
|
|
IdeUtilsPkgStrConsts,
|
|
// IdeProject
|
|
IdeProjectStrConsts,
|
|
// IDE
|
|
LazarusIDEStrConsts, EditorOptions, CheckLFMDlg, Project, SourceMarks,
|
|
// Converter
|
|
ConverterTypes, ConvertSettings, ReplaceNamesUnit,
|
|
ConvCodeTool, FormFileConv, UsedUnits;
|
|
|
|
type
|
|
|
|
{ TDFMConverter }
|
|
|
|
// Encapsulates some basic form file conversions.
|
|
TDFMConverter = class
|
|
private
|
|
fSettings: TConvertSettings;
|
|
fOrigFormat: TLRSStreamOriginalFormat;
|
|
public
|
|
constructor Create;
|
|
destructor Destroy; override;
|
|
function ConvertDfmToLfm(const aFilename: string): TModalResult;
|
|
function Convert(const DfmFilename: string;
|
|
DisplaySuccessMessage: Boolean): TModalResult;
|
|
public
|
|
property Settings: TConvertSettings read fSettings write fSettings;
|
|
end;
|
|
|
|
{ TLfmFixer }
|
|
|
|
TLFMFixer = class(TLFMChecker)
|
|
private
|
|
fCTLink: TCodeToolLink;
|
|
fSettings: TConvertSettings;
|
|
fUsedUnitsTool: TUsedUnitsTool;
|
|
// List of property values which need to be adjusted.
|
|
fHasMissingProperties: Boolean; // LFM file has unknown properties.
|
|
fHasMissingObjectTypes: Boolean; // LFM file has unknown object types.
|
|
// References to controls in UI:
|
|
fPropReplaceGrid: TStringGrid;
|
|
fTypeReplaceGrid: TStringGrid;
|
|
function ReplaceAndRemoveAll: TModalResult;
|
|
function ReplaceTopOffsets(aSrcOffsets: TList): TModalResult;
|
|
function AddNewProps(aNewProps: TList): TModalResult;
|
|
// Fill StringGrids with missing properties and types from fLFMTree.
|
|
procedure FillReplaceGrids;
|
|
function ShowConvertLFMWizard: TModalResult;
|
|
protected
|
|
function FixMissingComponentClasses(aMissingTypes: TClassList): TModalResult; override;
|
|
procedure LoadLFM;
|
|
public
|
|
constructor Create(ACTLink: TCodeToolLink; ALFMBuffer: TCodeBuffer);
|
|
destructor Destroy; override;
|
|
function ConvertAndRepair: TModalResult;
|
|
public
|
|
property Settings: TConvertSettings read fSettings write fSettings;
|
|
property UsedUnitsTool: TUsedUnitsTool read fUsedUnitsTool write fUsedUnitsTool;
|
|
end;
|
|
|
|
|
|
{ TFixLFMDialog }
|
|
|
|
TFixLFMDialog = class(TForm)
|
|
CancelButton: TBitBtn;
|
|
ErrorsGroupBox: TGroupBox;
|
|
ErrorsListBox: TListBox;
|
|
TypeReplaceGrid: TStringGrid;
|
|
PropertyReplaceGroupBox: TGroupBox;
|
|
NoteLabel: TLabel;
|
|
LFMGroupBox: TGroupBox;
|
|
LFMSynEdit: TSynEdit;
|
|
BtnPanel: TPanel;
|
|
ReplaceAllButton: TBitBtn;
|
|
Splitter1: TSplitter;
|
|
PropReplaceGrid: TStringGrid;
|
|
Splitter2: TSplitter;
|
|
Splitter3: TSplitter;
|
|
PropertiesText: TStaticText;
|
|
TypesText: TStaticText;
|
|
SynLFMSyn1: TSynLFMSyn;
|
|
procedure ErrorsListBoxClick(Sender: TObject);
|
|
procedure ReplaceAllButtonClick(Sender: TObject);
|
|
procedure LFMSynEditSpecialLineMarkup(Sender: TObject;
|
|
Line: integer; var Special: boolean; AMarkup: TSynSelectedColor);
|
|
procedure CheckLFMDialogCREATE(Sender: TObject);
|
|
private
|
|
fLfmFixer: TLFMFixer;
|
|
public
|
|
constructor Create(AOwner: TComponent; ALfmFixer: TLFMFixer); reintroduce;
|
|
destructor Destroy; override;
|
|
end;
|
|
|
|
|
|
implementation
|
|
|
|
|
|
{$R *.lfm}
|
|
|
|
function IsMissingType(LFMError: TLFMError): boolean;
|
|
begin
|
|
with LFMError do
|
|
Result:=(ErrorType in [lfmeIdentifierNotFound,lfmeMissingRoot])
|
|
and (Node is TLFMObjectNode)
|
|
and (TLFMObjectNode(Node).TypeName<>'');
|
|
end;
|
|
|
|
function FixWideString(aInStream, aOutStream: TMemoryStream): TModalResult;
|
|
// Convert Windows WideString syntax (#xxx) to UTF8
|
|
|
|
function UnicodeNumber(const InS: string; var Ind: integer): string;
|
|
// Convert the number to UTF8
|
|
var
|
|
Start, c: Integer;
|
|
begin
|
|
Inc(Ind); // Skip '#'
|
|
Start:=Ind;
|
|
while InS[Ind] in ['0'..'9'] do
|
|
Inc(Ind); // Collect numbers
|
|
c:=StrToInt(Copy(InS, Start, Ind-Start));
|
|
if c>255 then
|
|
Result:=UnicodeToUTF8(c)
|
|
else
|
|
Result:=SysToUTF8(chr(c));
|
|
end;
|
|
|
|
function FixControlChars(const s:string): string;
|
|
var
|
|
i: Integer;
|
|
InControl: boolean;
|
|
begin
|
|
Result := '';
|
|
InControl := false;
|
|
for i:=1 to Length(s) do begin
|
|
if s[i] < #32 then begin
|
|
if not InControl then
|
|
result := result + '''';
|
|
result := result + '#' + IntToStr(ord(s[i]));
|
|
InControl := true;
|
|
end else begin
|
|
if InControl then begin
|
|
result := result + '''';
|
|
InControl := false;
|
|
end;
|
|
Result := Result + s[i];
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function CollectString(const InS: string; var Ind: integer): string;
|
|
// Collect a string composed of quoted strings and unicode numbers like #xxx
|
|
var
|
|
InQuote: Boolean;
|
|
ch: Char;
|
|
begin
|
|
Result:='';
|
|
InQuote:=False;
|
|
repeat
|
|
ch:=InS[Ind];
|
|
if ch in [#13,#10] then Break;
|
|
if ch = '''' then begin
|
|
InQuote:=not InQuote; // Toggle quote
|
|
Inc(Ind);
|
|
end
|
|
else if InQuote then begin
|
|
Result:=Result+ch; // Inside quotes copy characters as is.
|
|
Inc(Ind);
|
|
end
|
|
else if ch = '#' then
|
|
Result:=Result+UnicodeNumber(InS, Ind)
|
|
else
|
|
Break;
|
|
until False;
|
|
Result:=FixControlChars(QuotedStr(Result));
|
|
end;
|
|
|
|
var
|
|
InS, OutS: string;
|
|
i: Integer;
|
|
begin
|
|
Result:=mrOk;
|
|
OutS:='';
|
|
aInStream.Position:=0;
|
|
SetLength(InS{%H-}, aInStream.Size);
|
|
aInStream.Read(InS[1],length(InS));
|
|
i := 1;
|
|
while i <= Length(InS) do begin
|
|
if InS[i] in ['''', '#'] then
|
|
OutS:=OutS+CollectString(InS, i)
|
|
else begin
|
|
OutS:=OutS+InS[i];
|
|
Inc(i);
|
|
end;
|
|
end;
|
|
// Write data to a new stream.
|
|
aOutStream.Write(OutS[1], Length(OutS));
|
|
end;
|
|
|
|
{ TDFMConverter }
|
|
|
|
constructor TDFMConverter.Create;
|
|
begin
|
|
inherited Create;
|
|
end;
|
|
|
|
destructor TDFMConverter.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TDFMConverter.Convert(const DfmFilename: string;
|
|
DisplaySuccessMessage: Boolean): TModalResult;
|
|
var
|
|
s: String;
|
|
Urgency: TMessageLineUrgency;
|
|
begin
|
|
Result:=ConvertDfmToLfm(DfmFilename);
|
|
if Result=mrOK then begin
|
|
if fOrigFormat=sofBinary then begin
|
|
if DisplaySuccessMessage then
|
|
s:=Format(lisFileSIsConvertedToTextFormat, [DfmFilename])
|
|
else
|
|
s:='';
|
|
Urgency:=mluHint;
|
|
end
|
|
else begin
|
|
s:=Format(lisFileSHasIncorrectSyntax, [DfmFilename]);
|
|
Urgency:=mluError;
|
|
end;
|
|
if s <> '' then begin
|
|
if Assigned(fSettings) then
|
|
fSettings.AddLogLine(Urgency, s, DfmFilename)
|
|
else
|
|
ShowMessage(s);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TDFMConverter.ConvertDfmToLfm(const aFilename: string): TModalResult;
|
|
var
|
|
DFMStream, LFMStream, Utf8LFMStream: TMemoryStream;
|
|
begin
|
|
Result:=mrOk;
|
|
DFMStream:=TMemoryStream.Create;
|
|
LFMStream:=TMemoryStream.Create;
|
|
Utf8LFMStream:=TMemoryStream.Create;
|
|
try
|
|
// Note: The file is copied from DFM file earlier. Load it.
|
|
try
|
|
DFMStream.LoadFromFile(aFilename);
|
|
except
|
|
on E: Exception do begin
|
|
Result:=QuestionDlg(lisCodeToolsDefsReadError, Format(
|
|
lisUnableToReadFileError, [aFilename, LineEnding, E.Message]),
|
|
mtError,[mrIgnore,mrAbort],0);
|
|
if Result=mrIgnore then // The caller will continue like nothing happened.
|
|
Result:=mrOk;
|
|
exit;
|
|
end;
|
|
end;
|
|
fOrigFormat:=TestFormStreamFormat(DFMStream);
|
|
// converting dfm file, without renaming unit -> keep case...
|
|
try
|
|
FormDataToText(DFMStream, LFMStream, fOrigFormat);
|
|
except
|
|
on E: Exception do begin
|
|
Result:=QuestionDlg(lisFormatError,
|
|
Format(lisUnableToConvertFileError, [aFilename, LineEnding, E.Message]),
|
|
mtError,[mrIgnore,mrAbort],0);
|
|
if Result=mrIgnore then
|
|
Result:=mrOk;
|
|
exit;
|
|
end;
|
|
end;
|
|
// Convert Windows WideString syntax (#xxx) to UTF8
|
|
FixWideString(LFMStream, Utf8LFMStream);
|
|
// Save the converted file.
|
|
try
|
|
Utf8LFMStream.SaveToFile(ChangeFileExt(aFilename, '.lfm'));
|
|
except
|
|
on E: Exception do begin
|
|
Result:=MessageDlg(lisCodeToolsDefsWriteError,
|
|
Format(lisUnableToWriteFileError, [aFilename, LineEnding, E.Message]),
|
|
mtError,[mbIgnore,mbAbort],0);
|
|
if Result=mrIgnore then
|
|
Result:=mrOk;
|
|
exit;
|
|
end;
|
|
end;
|
|
finally
|
|
Utf8LFMStream.Free;
|
|
LFMSTream.Free;
|
|
DFMStream.Free;
|
|
end;
|
|
end;
|
|
|
|
|
|
{ TLFMFixer }
|
|
|
|
constructor TLFMFixer.Create(ACTLink: TCodeToolLink; ALFMBuffer: TCodeBuffer);
|
|
begin
|
|
inherited Create(ACTLink.Code, ALFMBuffer);
|
|
fCTLink:=ACTLink;
|
|
fHasMissingProperties:=false;
|
|
fHasMissingObjectTypes:=false;
|
|
end;
|
|
|
|
destructor TLFMFixer.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
function TLFMFixer.ReplaceAndRemoveAll: TModalResult;
|
|
// Replace or remove properties and types based on values in grid.
|
|
// Returns mrRetry if some types were changed and a new scan is needed,
|
|
// mrOK if no types were changed, and mrCancel if there was an error.
|
|
var
|
|
AutoInc: integer;
|
|
|
|
function SolveAutoInc(AIdent: string): string;
|
|
begin
|
|
if Pos('$autoinc', AIdent)>0 then begin
|
|
Inc(AutoInc);
|
|
Result:=StringReplace(AIdent, '$autoinc', IntToStr(AutoInc), [rfReplaceAll]);
|
|
end
|
|
else
|
|
Result:=AIdent;
|
|
end;
|
|
|
|
procedure InitClassCompletion;
|
|
begin
|
|
with fCTLink.CodeTool do
|
|
if not Assigned(CodeCompleteClassNode) then begin // Do only at first time.
|
|
CodeCompleteClassNode:=FindClassNodeInInterface(
|
|
TLFMObjectNode(fLFMTree.Root).TypeName,true,false,true);
|
|
CodeCompleteSrcChgCache:=fCTLink.SrcCache;
|
|
end;
|
|
end;
|
|
|
|
var
|
|
CurError: TLFMError;
|
|
TheNode: TLFMTreeNode;
|
|
ObjNode: TLFMObjectNode;
|
|
// Property / Type name --> replacement name.
|
|
PropReplacements: TStringToStringTree;
|
|
TypeReplacements: TStringToStringTree;
|
|
// List of TLFMChangeEntry objects.
|
|
ChgEntryRepl: TObjectList;
|
|
OldIdent, NewIdent: string;
|
|
StartPos, EndPos: integer;
|
|
begin
|
|
Result:=mrOK;
|
|
AutoInc:=0;
|
|
fCTLink.CodeTool.CodeCompleteClassNode:=Nil;
|
|
ChgEntryRepl:=TObjectList.Create;
|
|
PropReplacements:=TStringToStringTree.Create(false);
|
|
TypeReplacements:=TStringToStringTree.Create(false);
|
|
try
|
|
// Collect (maybe edited) properties from StringGrid to map.
|
|
FromGridToMap(PropReplacements, fPropReplaceGrid);
|
|
FromGridToMap(TypeReplacements, fTypeReplaceGrid, false);
|
|
// Replace each missing property / type or delete it if no replacement.
|
|
CurError:=fLFMTree.LastError;
|
|
while CurError<>nil do begin
|
|
TheNode:=CurError.FindContextNode;
|
|
if (TheNode<>nil) and (TheNode.Parent<>nil) then
|
|
begin
|
|
if CurError.ErrorType=lfmeIdentifierMissingInCode then
|
|
begin
|
|
// Missing component variable, must be added to pascal sources
|
|
ObjNode:=CurError.Node as TLFMObjectNode;
|
|
InitClassCompletion;
|
|
NewIdent:=ObjNode.Name+':'+ObjNode.TypeName;
|
|
fCTLink.CodeTool.AddClassInsertion(UpperCase(ObjNode.Name),
|
|
NewIdent+';', ObjNode.Name, ncpPublishedVars);
|
|
fSettings.AddLogLine(mluNote,
|
|
Format(lisAddedMissingObjectSToPascalSource, [NewIdent]),
|
|
fUsedUnitsTool.Filename);
|
|
end
|
|
else if IsMissingType(CurError) then
|
|
begin
|
|
// Object type
|
|
ObjNode:=CurError.Node as TLFMObjectNode;
|
|
OldIdent:=ObjNode.TypeName;
|
|
NewIdent:=SolveAutoInc(TypeReplacements[OldIdent]);
|
|
// Keep the old class name if no replacement.
|
|
if NewIdent<>'' then begin
|
|
StartPos:=ObjNode.TypeNamePosition;
|
|
EndPos:=StartPos+Length(OldIdent);
|
|
fSettings.AddLogLine(mluNote,
|
|
Format(lisReplacedTypeSWithS, [OldIdent, NewIdent]),
|
|
fUsedUnitsTool.Filename);
|
|
AddReplacement(ChgEntryRepl,StartPos,EndPos,NewIdent);
|
|
Result:=mrRetry;
|
|
end;
|
|
end
|
|
else begin
|
|
// Property
|
|
TheNode.FindIdentifier(StartPos,EndPos);
|
|
if StartPos>0 then begin
|
|
OldIdent:=copy(fLFMBuffer.Source,StartPos,EndPos-StartPos);
|
|
NewIdent:=SolveAutoInc(PropReplacements[OldIdent]);
|
|
// Delete the whole property line if no replacement.
|
|
if NewIdent='' then begin
|
|
FindNiceNodeBounds(TheNode,StartPos,EndPos);
|
|
fSettings.AddLogLine(mluNote, Format(lisRemovedPropertyS, [OldIdent]),
|
|
fUsedUnitsTool.Filename);
|
|
end
|
|
else
|
|
fSettings.AddLogLine(mluNote,
|
|
Format(lisReplacedPropertySWithS, [OldIdent, NewIdent]));
|
|
AddReplacement(ChgEntryRepl,StartPos,EndPos,NewIdent);
|
|
Result:=mrRetry;
|
|
end;
|
|
end;
|
|
end;
|
|
CurError:=CurError.PrevError;
|
|
end;
|
|
// Apply replacements to LFM.
|
|
if not ApplyReplacements(ChgEntryRepl) then
|
|
exit(mrCancel);
|
|
// Apply added variables to pascal class definition.
|
|
with fCTLink.CodeTool do
|
|
if Assigned(CodeCompleteClassNode) then
|
|
if not ApplyClassCompletion(false) then
|
|
exit(mrCancel);
|
|
// Apply replacement types also to pascal source.
|
|
if TypeReplacements.Tree.Count>0 then
|
|
if not CodeToolBoss.RetypeClassVariables(fPascalBuffer,
|
|
TLFMObjectNode(fLFMTree.Root).TypeName, TypeReplacements, false, true) then
|
|
Result:=mrCancel;
|
|
finally
|
|
TypeReplacements.Free;
|
|
PropReplacements.Free;
|
|
ChgEntryRepl.Free;
|
|
end;
|
|
end;
|
|
|
|
function TLFMFixer.ReplaceTopOffsets(aSrcOffsets: TList): TModalResult;
|
|
// Replace top coordinates of controls in visual containers.
|
|
var
|
|
TopOffs: TSrcPropOffset;
|
|
VisOffs: TVisualOffset;
|
|
OldNum, NewNum, Len, ind, i: integer;
|
|
begin
|
|
Result:=mrOK;
|
|
// Add offset to top coordinates.
|
|
for i:=aSrcOffsets.Count-1 downto 0 do begin
|
|
TopOffs:=TSrcPropOffset(aSrcOffsets[i]);
|
|
if fSettings.CoordOffsets.Find(TopOffs.ParentType, ind) then begin
|
|
VisOffs:=fSettings.CoordOffsets[ind];
|
|
Len:=0;
|
|
while fLFMBuffer.Source[TopOffs.StartPos+Len] in ['-', '0'..'9'] do
|
|
Inc(Len);
|
|
try
|
|
OldNum:=StrToInt(Copy(fLFMBuffer.Source, TopOffs.StartPos, Len));
|
|
except on EConvertError do
|
|
OldNum:=0;
|
|
end;
|
|
NewNum:=OldNum-VisOffs.ByProperty(TopOffs.PropName);
|
|
if NewNum<0 then
|
|
NewNum:=0;
|
|
fLFMBuffer.Replace(TopOffs.StartPos, Len, IntToStr(NewNum));
|
|
fSettings.AddLogLine(mluNote, Format(lisChangedSCoordOfSFromDToDInsideS,
|
|
[TopOffs.PropName, TopOffs.ChildType, OldNum, NewNum, TopOffs.ParentType]),
|
|
fUsedUnitsTool.Filename);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TLFMFixer.AddNewProps(aNewProps: TList): TModalResult;
|
|
// Add new property to the lfm file.
|
|
var
|
|
Entry: TAddPropEntry;
|
|
i: integer;
|
|
begin
|
|
Result:=mrOK;
|
|
for i:=aNewProps.Count-1 downto 0 do begin
|
|
Entry:=TAddPropEntry(aNewProps[i]);
|
|
fLFMBuffer.Replace(Entry.StartPos, Entry.EndPos-Entry.StartPos,
|
|
Entry.NewPrefix+Entry.NewText);
|
|
fSettings.AddLogLine(mluNote,
|
|
Format(lisAddedPropertySForS, [Entry.NewText, Entry.ParentType]),
|
|
fUsedUnitsTool.Filename);
|
|
end;
|
|
end;
|
|
|
|
procedure TLFMFixer.FillReplaceGrids;
|
|
var
|
|
PropUpdater: TGridUpdater;
|
|
TypeUpdater: TGridUpdater;
|
|
CurError: TLFMError;
|
|
OldIdent, NewIdent: string;
|
|
begin
|
|
fHasMissingProperties:=false;
|
|
fHasMissingObjectTypes:=false;
|
|
// ReplaceTypes is used for properties just in case it will provide some.
|
|
PropUpdater:=TGridUpdater.Create(fSettings.ReplaceTypes, fPropReplaceGrid);
|
|
TypeUpdater:=TGridUpdater.Create(fSettings.ReplaceTypes, fTypeReplaceGrid);
|
|
try
|
|
if fLFMTree<>nil then begin
|
|
CurError:=fLFMTree.FirstError;
|
|
while CurError<>nil do begin
|
|
if IsMissingType(CurError) then begin
|
|
OldIdent:=(CurError.Node as TLFMObjectNode).TypeName;
|
|
NewIdent:=TypeUpdater.AddUnique(OldIdent); // Add each type only once.
|
|
if NewIdent<>'' then
|
|
fHasMissingObjectTypes:=true;
|
|
end
|
|
else if fSettings.PropReplaceMode<>rlDisabled then begin
|
|
OldIdent:=CurError.Node.GetIdentifier;
|
|
PropUpdater.AddUnique(OldIdent); // Add each property only once.
|
|
fHasMissingProperties:=true;
|
|
end;
|
|
CurError:=CurError.NextError;
|
|
end;
|
|
end;
|
|
finally
|
|
TypeUpdater.Free;
|
|
PropUpdater.Free;
|
|
end;
|
|
end;
|
|
|
|
function TLFMFixer.ShowConvertLFMWizard: TModalResult;
|
|
var
|
|
FixLFMDialog: TFixLFMDialog;
|
|
begin
|
|
Result:=mrCancel;
|
|
FixLFMDialog:=TFixLFMDialog.Create(nil, self);
|
|
try
|
|
fLFMSynEdit:=FixLFMDialog.LFMSynEdit;
|
|
fErrorsListBox:=FixLFMDialog.ErrorsListBox;
|
|
fPropReplaceGrid:=FixLFMDialog.PropReplaceGrid;
|
|
fTypeReplaceGrid:=FixLFMDialog.TypeReplaceGrid;
|
|
LoadLFM;
|
|
if ((fSettings.PropReplaceMode=rlAutomatic) or not fHasMissingProperties)
|
|
and ((fSettings.TypeReplaceMode=raAutomatic) or not fHasMissingObjectTypes) then
|
|
Result:=ReplaceAndRemoveAll // Can return mrRetry.
|
|
else begin
|
|
Result:=FixLFMDialog.ShowModal;
|
|
end;
|
|
finally
|
|
FixLFMDialog.Free;
|
|
end;
|
|
end;
|
|
|
|
function TLFMFixer.FixMissingComponentClasses(aMissingTypes: TClassList): TModalResult;
|
|
// This is called from TLFMChecker.FindAndFixMissingComponentClasses.
|
|
// Add needed units to uses section using methods already defined in fUsedUnitsTool.
|
|
var
|
|
RegComp: TRegisteredComponent;
|
|
//ClassUnitInfo: TUnitInfo;
|
|
i: Integer;
|
|
NeededUnitName: String;
|
|
begin
|
|
Result:=mrOK;
|
|
if not Assigned(fUsedUnitsTool) then Exit;
|
|
for i := 0 to aMissingTypes.Count-1 do
|
|
begin
|
|
RegComp:=IDEComponentPalette.FindRegComponent(aMissingTypes[i]);
|
|
NeededUnitName:='';
|
|
if Assigned(RegComp) then
|
|
begin
|
|
if RegComp.ComponentClass<>nil then
|
|
begin
|
|
NeededUnitName:=RegComp.ComponentClass.UnitName;
|
|
if NeededUnitName='' then
|
|
NeededUnitName:=RegComp.GetUnitName;
|
|
end;
|
|
end
|
|
else begin
|
|
Assert(False, 'TLFMFixer.FixMissingComponentClasses: RegComp=Nil');
|
|
{ ClassUnitInfo:=Project1.UnitWithComponentClass(aMissingTypes[i] as TComponentClass);
|
|
if ClassUnitInfo<>nil then
|
|
NeededUnitName:=ClassUnitInfo.GetUsesUnitName; }
|
|
end;
|
|
if (NeededUnitName<>'')
|
|
and fUsedUnitsTool.AddUnitImmediately(NeededUnitName) then
|
|
begin
|
|
Result:=mrRetry; // Caller must check LFM validity again
|
|
fUsedUnitsTool.MaybeAddPackageDep(NeededUnitName);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TLFMFixer.LoadLFM;
|
|
begin
|
|
inherited LoadLFM;
|
|
FillReplaceGrids; // Fill both ReplaceGrids.
|
|
end;
|
|
|
|
function TLFMFixer.ConvertAndRepair: TModalResult;
|
|
const
|
|
MaxLoopCount = 50;
|
|
var
|
|
ConvTool: TConvDelphiCodeTool;
|
|
FormFileTool: TFormFileConverter;
|
|
SrcCoordOffs: TObjectList;
|
|
SrcNewProps: TObjectList;
|
|
LoopCount: integer;
|
|
begin
|
|
Result:=mrCancel;
|
|
fLFMTree:=DefaultLFMTrees.GetLFMTree(fLFMBuffer, true);
|
|
if not fLFMTree.ParseIfNeeded then exit;
|
|
// Change a type that main form inherits from to a fall-back type if needed.
|
|
ConvTool:=TConvDelphiCodeTool.Create(fCTLink);
|
|
try
|
|
if not ConvTool.FixMainClassAncestor(TLFMObjectNode(fLFMTree.Root).TypeName,
|
|
fSettings.ReplaceTypes) then exit;
|
|
finally
|
|
ConvTool.Free;
|
|
end;
|
|
LoopCount:=0; // Prevent possible eternal loops with a counter
|
|
repeat
|
|
repeat
|
|
DebugLn('TLFMFixer.ConvertAndRepair: Checking LFM for '+fPascalBuffer.Filename);
|
|
if not fLFMTree.ParseIfNeeded then exit;
|
|
if CodeToolBoss.CheckLFM(fPascalBuffer, fLFMBuffer, fLFMTree,
|
|
fRootMustBeClassInUnit, fRootMustBeClassInIntf, fObjectsMustExist) then
|
|
Result:=mrOk
|
|
else // Rename/remove properties and types interactively.
|
|
Result:=ShowConvertLFMWizard; // Can return mrRetry.
|
|
Inc(LoopCount); // Increment counter in inner loop
|
|
until (Result in [mrOK, mrCancel]) or (LoopCount>MaxLoopCount);
|
|
|
|
// Check for missing object types and add units as needed.
|
|
if not fLFMTree.ParseIfNeeded then
|
|
Exit(mrCancel);
|
|
if CodeToolBoss.CheckLFM(fPascalBuffer, fLFMBuffer, fLFMTree,
|
|
fRootMustBeClassInUnit, fRootMustBeClassInIntf, fObjectsMustExist)
|
|
then
|
|
Result:=mrOk
|
|
else begin
|
|
Result:=FindAndFixMissingComponentClasses; // Can return mrRetry.
|
|
if Result=mrRetry then
|
|
DebugLn('TLFMFixer.ConvertAndRepair: Added unit to uses section -> another loop');
|
|
end;
|
|
Inc(LoopCount); // Increment also in outer loop
|
|
until (Result in [mrOK, mrAbort]) or (LoopCount>MaxLoopCount);
|
|
|
|
// Fix top offsets of some components in visual containers
|
|
if (Result=mrOK) and (fSettings.CoordOffsMode=rsEnabled) then
|
|
begin
|
|
FormFileTool:=TFormFileConverter.Create(fCTLink, fLFMBuffer);
|
|
SrcCoordOffs:=TObjectList.Create;
|
|
SrcNewProps:=TObjectList.Create;
|
|
try
|
|
FormFileTool.VisOffsets:=fSettings.CoordOffsets;
|
|
FormFileTool.SrcCoordOffs:=SrcCoordOffs;
|
|
FormFileTool.SrcNewProps:=SrcNewProps;
|
|
Result:=FormFileTool.Convert;
|
|
if Result=mrOK then begin
|
|
Result:=ReplaceTopOffsets(SrcCoordOffs);
|
|
if Result=mrOK then
|
|
Result:=AddNewProps(SrcNewProps);
|
|
end;
|
|
finally
|
|
SrcNewProps.Free;
|
|
SrcCoordOffs.Free;
|
|
FormFileTool.Free;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
{ TFixLFMDialog }
|
|
|
|
constructor TFixLFMDialog.Create(AOwner: TComponent; ALfmFixer: TLFMFixer);
|
|
begin
|
|
inherited Create(AOwner);
|
|
fLfmFixer:=ALfmFixer;
|
|
end;
|
|
|
|
destructor TFixLFMDialog.Destroy;
|
|
begin
|
|
inherited Destroy;
|
|
end;
|
|
|
|
procedure TFixLFMDialog.CheckLFMDialogCREATE(Sender: TObject);
|
|
begin
|
|
Caption:=lisFixLFMFile;
|
|
Position:=poScreenCenter;
|
|
NoteLabel.Caption:=lisLFMFileContainsInvalidProperties;
|
|
ErrorsGroupBox.Caption:=lisErrors;
|
|
LFMGroupBox.Caption:=lisLFMFile;
|
|
PropertyReplaceGroupBox.Caption:=lisReplacements;
|
|
PropertiesText.Caption:=lisProperties;
|
|
TypesText.Caption:=lisTypes;
|
|
ReplaceAllButton.Caption:=lisReplaceRemoveUnknown;
|
|
IDEImages.AssignImage(ReplaceAllButton, 'laz_refresh');
|
|
EditorOpts.GetHighlighterSettings(SynLFMSyn1);
|
|
EditorOpts.GetSynEditSettings(LFMSynEdit);
|
|
end;
|
|
|
|
procedure TFixLFMDialog.ReplaceAllButtonClick(Sender: TObject);
|
|
begin
|
|
ModalResult:=fLfmFixer.ReplaceAndRemoveAll;
|
|
end;
|
|
|
|
procedure TFixLFMDialog.ErrorsListBoxClick(Sender: TObject);
|
|
begin
|
|
fLfmFixer.JumpToError(fLfmFixer.FindListBoxError);
|
|
end;
|
|
|
|
procedure TFixLFMDialog.LFMSynEditSpecialLineMarkup(Sender: TObject;
|
|
Line: integer; var Special: boolean; AMarkup: TSynSelectedColor);
|
|
var
|
|
CurError: TLFMError;
|
|
begin
|
|
CurError:=fLfmFixer.fLFMTree.FindErrorAtLine(Line);
|
|
if CurError = nil then Exit;
|
|
Special := True;
|
|
EditorOpts.SetMarkupColor(SynLFMSyn1, ahaErrorLine, AMarkup);
|
|
end;
|
|
|
|
|
|
end.
|
|
|