2005-08-26 18:29:39 +00:00
|
|
|
unit UnitCodeInspector;
|
|
|
|
|
|
|
|
interface
|
|
|
|
|
|
|
|
uses SysUtils, Classes, Windows, JvInspector, UnitMainTools, Contnrs;
|
|
|
|
|
|
|
|
type
|
|
|
|
TSelectionTextList = class(TStringList)
|
|
|
|
private
|
|
|
|
FSelected: Integer;
|
|
|
|
function GetSelectedText: string;
|
|
|
|
function GetSelected: Integer;
|
|
|
|
procedure SetSelectedText(const Value: string);
|
|
|
|
procedure SetSelected(const Value: Integer);
|
|
|
|
public
|
|
|
|
property Selected: Integer read GetSelected write SetSelected;
|
|
|
|
property SelectedText: string read GetSelectedText write SetSelectedText;
|
|
|
|
end;
|
|
|
|
|
|
|
|
TJvInspectorSelectionTextListItem = class(TJvCustomInspectorItem)
|
|
|
|
protected
|
|
|
|
function GetDisplayValue: string; override;
|
|
|
|
procedure GetValueList(const Strings: TStrings); override;
|
|
|
|
procedure SetDisplayValue(const Value: string); override;
|
|
|
|
procedure SetFlags(const Value: TInspectorItemFlags); override;
|
|
|
|
end;
|
|
|
|
|
|
|
|
TStringWrapper = class(TObject)
|
|
|
|
public
|
|
|
|
Value: string;
|
2005-09-07 22:20:28 +00:00
|
|
|
constructor Create(const AValue: string); reintroduce;
|
2005-09-10 21:55:22 +00:00
|
|
|
destructor Destroy; reintroduce;
|
2005-08-26 18:29:39 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
TSTLWrapper = class(TObject)
|
|
|
|
public
|
|
|
|
STL: TSelectionTextList;
|
|
|
|
Value: String;
|
2005-09-07 22:20:28 +00:00
|
|
|
constructor Create(const ASTL: TSelectionTextList; const AValue: String); reintroduce;
|
|
|
|
destructor Destroy; reintroduce;
|
2005-08-26 18:29:39 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
function AddField(eName, eCategory, eValue: String): TJvCustomInspectorItem;
|
|
|
|
function AddCombo(eName, eCategory, eValue: String; eValues: array of string): TJvCustomInspectorItem;
|
|
|
|
|
|
|
|
procedure UpdateCI;
|
2005-09-07 22:20:28 +00:00
|
|
|
procedure UpdateCI_Pawn;
|
2005-08-26 18:29:39 +00:00
|
|
|
|
|
|
|
var eFormatSettings: String;
|
|
|
|
eAllIncluded: TStringArray;
|
|
|
|
|
|
|
|
FItems: TObjectList;
|
|
|
|
STLItem: TSelectionTextList;
|
|
|
|
|
|
|
|
implementation
|
|
|
|
|
|
|
|
uses UnitfrmMain, UnitLanguages, UnitCodeUtils, UnitMenuGenerators,
|
|
|
|
UnitPlugins;
|
|
|
|
|
|
|
|
var eBraceTexts: TStringList;
|
|
|
|
|
|
|
|
{ "Combobox"-Item }
|
|
|
|
|
|
|
|
function TSelectionTextList.GetSelected: Integer;
|
|
|
|
begin
|
|
|
|
if FSelected < -1 then
|
|
|
|
FSelected := -1
|
|
|
|
else if FSelected >= Count then
|
|
|
|
FSelected := Count - 1;
|
|
|
|
Result := FSelected;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TSelectionTextList.GetSelectedText: string;
|
|
|
|
begin
|
|
|
|
if Selected >= 0 then
|
|
|
|
Result := Strings[Selected]
|
|
|
|
else
|
|
|
|
Result := '';
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TSelectionTextList.SetSelected(const Value: Integer);
|
|
|
|
begin
|
|
|
|
FSelected := Value;
|
|
|
|
GetSelected; // adjust FSelected
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TSelectionTextList.SetSelectedText(const Value: string);
|
|
|
|
begin
|
|
|
|
FSelected := IndexOf(Value);
|
|
|
|
end;
|
|
|
|
|
|
|
|
function TJvInspectorSelectionTextListItem.GetDisplayValue: string;
|
|
|
|
begin
|
|
|
|
Result := TSelectionTextList(Data.AsOrdinal).SelectedText;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvInspectorSelectionTextListItem.GetValueList(const Strings: TStrings);
|
|
|
|
begin
|
|
|
|
Strings.Assign(TSelectionTextList(Data.AsOrdinal));
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvInspectorSelectionTextListItem.SetDisplayValue(const Value: string);
|
|
|
|
begin
|
|
|
|
TSelectionTextList(Data.AsOrdinal).SelectedText := Value;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TJvInspectorSelectionTextListItem.SetFlags(const Value: TInspectorItemFlags);
|
|
|
|
begin
|
|
|
|
inherited SetFlags(Value + [iifValueList]);
|
|
|
|
end;
|
|
|
|
|
|
|
|
constructor TStringWrapper.Create(const AValue: string);
|
|
|
|
begin
|
|
|
|
inherited Create;
|
|
|
|
Value := AValue;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ Codeinspector Add Functions }
|
|
|
|
|
|
|
|
function AddCombo(eName, eCategory, eValue: String; eValues: array of string): TJvCustomInspectorItem;
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
eParent: TJvCustomInspectorItem;
|
|
|
|
Item: TSTLWrapper;
|
|
|
|
Found: Boolean;
|
|
|
|
begin
|
|
|
|
eParent := nil;
|
|
|
|
for i := 0 to frmMain.jviCode.Root.Count -1 do
|
|
|
|
begin
|
|
|
|
if (frmMain.jviCode.Root.Items[i].DisplayName = eCategory) and (frmMain.jviCode.Root.Items[i] is TJvInspectorCustomCategoryItem) then
|
|
|
|
begin
|
|
|
|
eParent := frmMain.jviCode.Root.Items[i];
|
|
|
|
Break;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
if eParent = nil then
|
|
|
|
begin
|
|
|
|
eParent := TJvInspectorCustomCategoryItem.Create(frmMain.jviCode.Root, nil);
|
|
|
|
eParent.DisplayName := eCategory;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if eName <> '' then
|
|
|
|
begin
|
|
|
|
STLItem := TSelectionTextList.Create;
|
|
|
|
Found := False;
|
|
|
|
for i := 0 to High(eValues) do begin
|
|
|
|
STLItem.Add(eValues[i]);
|
|
|
|
if eValues[i] = eValue then
|
|
|
|
Found := True;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if not Found then begin
|
|
|
|
STLItem.Add(eValue);
|
|
|
|
STLItem.Sort;
|
|
|
|
end;
|
|
|
|
|
|
|
|
Item := TSTLWrapper.Create(STLItem, eValue);
|
|
|
|
FItems.Add(Item);
|
|
|
|
FItems.Add(STLItem);
|
|
|
|
|
|
|
|
STLItem.SelectedText := Item.Value;
|
|
|
|
Result := TJvInspectorVarData.New(eParent, eName, TypeInfo(TSelectionTextList), @Item.STL);
|
|
|
|
frmMain.jviCode.Root.Sort;
|
|
|
|
eParent.Expanded := True;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
Result := nil;
|
|
|
|
frmMain.jviCode.Root.Sort;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function AddField(eName, eCategory, eValue: String): TJvCustomInspectorItem;
|
|
|
|
var
|
|
|
|
i: integer;
|
|
|
|
eParent: TJvCustomInspectorItem;
|
|
|
|
Item: TStringWrapper;
|
|
|
|
begin
|
|
|
|
eParent := nil;
|
|
|
|
for i := 0 to frmMain.jviCode.Root.Count -1 do
|
|
|
|
begin
|
|
|
|
if (frmMain.jviCode.Root.Items[i].DisplayName = eCategory) and (frmMain.jviCode.Root.Items[i] is TJvInspectorCustomCategoryItem) then
|
|
|
|
begin
|
|
|
|
eParent := frmMain.jviCode.Root.Items[i];
|
|
|
|
Break;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
if eParent = nil then
|
|
|
|
begin
|
|
|
|
eParent := TJvInspectorCustomCategoryItem.Create(frmMain.jviCode.Root, nil);
|
|
|
|
eParent.DisplayName := eCategory;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if eName <> '' then
|
|
|
|
begin
|
|
|
|
Item := TStringWrapper.Create(eValue); // StringWrapper erzeugen, damit der String erhalten bleibt
|
|
|
|
FItems.Add(Item); // und das Item in die Liste eintragen, damit kein Speicherleck entsteht
|
|
|
|
Result := TJvInspectorVarData.New(eParent, eName, TypeInfo(String), @Item.Value);
|
|
|
|
frmMain.jviCode.Root.Sort;
|
|
|
|
eParent.Expanded := True;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
begin
|
|
|
|
Result := nil;
|
|
|
|
frmMain.jviCode.Root.Sort;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
{ Parse Functions }
|
|
|
|
|
|
|
|
procedure UpdateCI;
|
|
|
|
begin
|
|
|
|
if not Plugin_UpdateCodeInspector(GetCurrLang.Name, ActiveDoc.FileName, frmMain.tsMain.Items[frmMain.tsMain.ActiveTabIndex].Caption, True) then exit;
|
|
|
|
|
|
|
|
if GetCurrLang.Name = 'Pawn' then begin
|
2005-09-07 22:20:28 +00:00
|
|
|
UpdateCI_Pawn;
|
2005-08-26 18:29:39 +00:00
|
|
|
Plugin_UpdateCodeInspector(GetCurrLang.Name, ActiveDoc.FileName, frmMain.tsMain.Items[frmMain.tsMain.ActiveTabIndex].Caption, False);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
2005-09-07 22:20:28 +00:00
|
|
|
procedure UpdateCI_Pawn;
|
2005-08-26 18:29:39 +00:00
|
|
|
procedure HideBracesAndStrings(var eStr: String);
|
|
|
|
begin
|
|
|
|
while Between(eStr, '{', '}') <> '' do begin
|
|
|
|
eBraceTexts.Add('{' + Between(eStr, '{', '}') + '}');
|
|
|
|
eStr := StringReplace(eStr, '{' + Between(eStr, '{', '}') + '}', #1 + IntToStr(eBraceTexts.Count) + #1, []);
|
|
|
|
end;
|
|
|
|
while CountChars(eStr, '"') > 1 do begin
|
|
|
|
eBraceTexts.Add('"' + StringReplace(Between(eStr, '"', '"'), ':', #3, [rfReplaceAll]) + '"');
|
|
|
|
eStr := StringReplace(eStr, '"' + Between(eStr, '"', '"') + '"', #2 + IntToStr(eBraceTexts.Count) + #2, []);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
function ShowBracesAndStrings(eStr: String): String;
|
|
|
|
var k: integer;
|
|
|
|
begin
|
|
|
|
while Between(eStr, #1, #1) <> '' do begin
|
|
|
|
k := StrToInt(Between(eStr, #1, #1));
|
|
|
|
eStr := StringReplace(eStr, #1 + IntToStr(k) + #1, eBraceTexts[k -1], []);
|
|
|
|
end;
|
|
|
|
while Between(eStr, #2, #2) <> '' do begin
|
|
|
|
k := StrToInt(Between(eStr, #2, #2));
|
|
|
|
eStr := StringReplace(eStr, #2 + IntToStr(k) + #2, eBraceTexts[k -1], []);
|
|
|
|
end;
|
|
|
|
Result := eStr;
|
|
|
|
end;
|
|
|
|
|
|
|
|
var eCurrLine, eBackupLine: String;
|
|
|
|
i, k: integer;
|
|
|
|
eStr: TStringList;
|
|
|
|
eVars, eConsts: Integer;
|
|
|
|
eVarName, eVarType, eVarValue: String;
|
|
|
|
begin
|
|
|
|
eBackupLine := frmMain.sciEditor.Lines[frmMain.sciEditor.GetCurrentLineNumber];
|
|
|
|
eCurrLine := Trim(StringReplace(eBackupLine, #9, #32, [rfReplaceAll]));
|
|
|
|
eCurrLine := RemoveStringsAndComments(eCurrLine, False);
|
|
|
|
eAllIncluded := GetAllIncludeFiles;
|
|
|
|
eStr := TStringList.Create;
|
|
|
|
eBraceTexts := TStringList.Create;
|
|
|
|
eVars := 0;
|
|
|
|
eConsts := 0;
|
|
|
|
frmMain.jviCode.Clear;
|
|
|
|
FItems.Clear;
|
|
|
|
|
|
|
|
eFormatSettings := '';
|
|
|
|
{ Constants and Variables }
|
|
|
|
if (IsAtStart('new', eCurrLine, False)) then begin // const or variable
|
|
|
|
Delete(eCurrLine, 1, 4);
|
|
|
|
|
|
|
|
// done? okay, split all items if there are more than one; and if not, it's okay...
|
|
|
|
HideBracesAndStrings(eCurrLine);
|
|
|
|
eStr.Text := StringReplace(eCurrLine, ',', #13, [rfReplaceAll]);
|
|
|
|
|
|
|
|
for i := 0 to eStr.Count - 1 do begin
|
|
|
|
eStr[i] := ShowBracesAndStrings(eStr[i]);
|
|
|
|
eVarType := '';
|
|
|
|
eVarValue := '';
|
|
|
|
|
|
|
|
if (Trim(eStr[i]) <> '') then begin
|
|
|
|
eVarName := Trim(RemoveSemicolon(eStr[i]));
|
|
|
|
if Pos(':', eVarName) <> 0 then begin
|
|
|
|
eVarType := Trim(Copy(eVarName, 1, Pos(':', eVarName) -1));
|
|
|
|
eVarName := Trim(Copy(eVarName, Pos(':', eVarName) +1, Length(eVarName)));
|
|
|
|
end;
|
|
|
|
|
|
|
|
if Pos('=', eVarName) <> 0 then begin // constant
|
|
|
|
Inc(eConsts, 1);
|
|
|
|
eFormatSettings := eFormatSettings + '-Constant ' + IntToStr(eConsts) + '-';
|
|
|
|
|
|
|
|
eVarValue := Trim(Copy(eVarName, Pos('=', eVarName) +1, Length(eVarName)));
|
|
|
|
eVarValue := StringReplace(eVarValue, #3, ':', [rfReplaceAll]);
|
|
|
|
eVarName := Trim(Copy(eVarName, 1, Pos('=', eVarName) -1));
|
|
|
|
|
|
|
|
AddField(lName, 'Constant ' + IntToStr(eConsts), eVarName);
|
|
|
|
if eVarType <> '' then
|
|
|
|
AddField(lType, 'Constant ' + IntToStr(eConsts), eVarType);
|
|
|
|
if eVarValue <> '' then
|
|
|
|
AddField(lValue, 'Constant ' + IntToStr(eConsts), eVarValue);
|
|
|
|
end
|
|
|
|
else begin // variable
|
|
|
|
Inc(eVars, 1);
|
|
|
|
eFormatSettings := eFormatSettings + '-Variable ' + IntToStr(eVars) + '-';
|
|
|
|
AddField(lName, 'Variable ' + IntToStr(eVars), eVarName);
|
|
|
|
if eVarType <> '' then
|
|
|
|
AddField(lType, 'Variable ' + IntToStr(eVars), eVarType);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
|
|
|
|
if frmMain.jviCode.Root.Count = 1 then
|
|
|
|
frmMain.jviCode.Root.Items[0].DisplayName := Copy(frmMain.jviCode.Root.Items[0].DisplayName, 1, Length(frmMain.jviCode.Root.Items[0].DisplayName) -2);
|
|
|
|
end
|
|
|
|
{ Conditions }
|
|
|
|
else if (IsAtStart('if', eCurrLine, False)) then begin
|
|
|
|
if (CountChars(eCurrLine, '(') = CountChars(eCurrLine, ')')) and (CountChars(eCurrLine, '(') <> 0) then begin
|
|
|
|
eCurrLine := Copy(eCurrLine, 1, GetMatchingBrace(eCurrLine) -1);
|
|
|
|
eCurrLine := Copy(eCurrLine, Pos('(', eCurrLine) +1, Length(eCurrLine));
|
|
|
|
eFormatSettings := StringReplace(eBackupLine, '(' + eCurrLine + ')', #1#3#3#7, []);
|
|
|
|
HideBracesAndStrings(eCurrLine);
|
|
|
|
eStr.Text := StringReplace(eCurrLine, '||', #13, [rfReplaceAll]);
|
|
|
|
k := eStr.Count -1; // from 0 to k -> OR operators
|
|
|
|
eStr.Text := StringReplace(eStr.Text, '&&', #13, [rfReplaceAll]);
|
|
|
|
|
|
|
|
for i := 0 to eStr.Count -1 do begin
|
|
|
|
eStr[i] := Trim(ShowBracesAndStrings(eStr[i]));
|
|
|
|
if (Pos('(', eStr[i]) = 1) and (Pos(')', eStr[i]) = Length(eStr[i])) then
|
|
|
|
eStr[i] := Copy(Copy(eStr[i], 2, Length(eStr[i])), 1, Length(eStr[i]) -1);
|
|
|
|
|
|
|
|
if eStr.Count = 1 then
|
|
|
|
eCurrLine := 'If-Condition'
|
|
|
|
else
|
|
|
|
eCurrLine := 'If-Condition ' + IntToStr(frmMain.jviCode.Root.Count +1);
|
|
|
|
|
|
|
|
if i <> eStr.Count -1 then begin
|
|
|
|
if i < k then
|
|
|
|
AddCombo('Operator', eCurrLine, '||', ['||', '&&'])
|
|
|
|
else
|
|
|
|
AddCombo('Operator', eCurrLine, '&&', ['||', '&&']);
|
|
|
|
end;
|
|
|
|
AddField('Condition', eCurrLine, eStr[i]);
|
|
|
|
end;
|
|
|
|
end
|
|
|
|
else
|
|
|
|
AddField('', 'Invalid condition', '');
|
|
|
|
end
|
|
|
|
{ Defined }
|
|
|
|
else if (IsAtStart('#define', eCurrLine, False)) then begin
|
|
|
|
eCurrLine := Trim(Copy(eCurrLine, 8, Length(eCurrLine)));
|
|
|
|
HideBracesAndStrings(eCurrLine);
|
|
|
|
eCurrLine := StringReplace(eCurrLine, #9, #32, [rfReplaceAll]);
|
|
|
|
eCurrLine := ShowBracesAndStrings(eCurrLine);
|
|
|
|
AddField('Name', 'Defined', Copy(eCurrLine, 1, Pos(#32, eCurrLine) -1));
|
|
|
|
eCurrLine := Trim(Copy(eCurrLine, Pos(#32, eCurrLine) +1, Length(eCurrLine)));
|
|
|
|
AddField('Value', 'Defined', eCurrLine);
|
|
|
|
end
|
|
|
|
{ Included }
|
|
|
|
else if (IsAtStart('#include', eCurrLine, False)) then begin
|
|
|
|
eCurrLine := Trim(StringReplace(eCurrLine, #9, #32, [rfReplaceAll]));
|
|
|
|
if Between(eCurrLine, '<', '>') <> '' then begin
|
|
|
|
eCurrLine := Between(eCurrLine, '<', '>');
|
|
|
|
eFormatSettings := StringReplace(eBackupLine, '<' + eCurrLine + '>', '<-Filename->', []);
|
|
|
|
end
|
|
|
|
else if Between(eCurrLine, '"', '"') <> '' then begin
|
|
|
|
eCurrLine := Between(eCurrLine, '"', '"');
|
|
|
|
eFormatSettings := StringReplace(eBackupLine, '"' + eCurrLine + '"', '"-Filename-"', []);
|
|
|
|
end
|
|
|
|
else begin
|
|
|
|
eCurrLine := Copy(eCurrLine, 9, Length(eCurrLine));
|
|
|
|
eFormatSettings := '#include -Filename-';
|
|
|
|
end;
|
|
|
|
eCurrLine := Trim(eCurrLine);
|
|
|
|
AddCombo('File', 'Included', eCurrLine, eAllIncluded);
|
|
|
|
end
|
|
|
|
{ Assignments }
|
|
|
|
else begin
|
|
|
|
if (Pos('=', eCurrLine) <> 0) then begin
|
|
|
|
eCurrLine := Trim(eCurrLine);
|
|
|
|
while Pos(eCurrLine[1], frmMain.sciEditor.WordChars + '[]') <> 0 do
|
|
|
|
Delete(eCurrLine, 1, 1);
|
|
|
|
eCurrLine := Trim(eCurrLine);
|
|
|
|
if Pos('=', eCurrLine) <= 2 then begin
|
|
|
|
while (Pos(Copy(eCurrLine, 1, 1), frmMain.sciEditor.WordChars + #32 + #9) = 0) and (Length(eCurrLine) <> 0) do begin
|
|
|
|
eFormatSettings := eFormatSettings + eCurrLine[1];
|
|
|
|
Delete(eCurrLine, 1, 1);
|
|
|
|
end;
|
|
|
|
eCurrLine := RemoveSemicolon(Trim(eBackupLine));
|
|
|
|
eCurrLine := StringReplace(eCurrLine, #9, #32, [rfReplaceAll]);
|
|
|
|
AddField('a', 'Assignment', Copy(eCurrLine, 1, Pos(eFormatSettings, eCurrLine) - Length(eFormatSettings)));
|
|
|
|
AddField('b', 'Assignment', Trim(Copy(eCurrLine, Pos(eFormatSettings, eCurrLine) + Length(eFormatSettings), Length(eCurrLine))));
|
|
|
|
AddField('Operator', 'Assignment', eFormatSettings);
|
|
|
|
end
|
|
|
|
else
|
|
|
|
AddField('', 'No information available.', '');
|
|
|
|
end
|
|
|
|
else
|
|
|
|
AddField('', 'No information available.', '');
|
|
|
|
end;
|
|
|
|
eBraceTexts.Free;
|
2005-09-07 22:20:28 +00:00
|
|
|
SetLength(eAllIncluded, 0);
|
|
|
|
eStr.Free;
|
2005-08-26 18:29:39 +00:00
|
|
|
end;
|
|
|
|
|
|
|
|
{ TSTLWrapper }
|
|
|
|
|
|
|
|
constructor TSTLWrapper.Create(const ASTL: TSelectionTextList;
|
|
|
|
const AValue: String);
|
|
|
|
begin
|
|
|
|
STL := ASTL;
|
|
|
|
Value := AValue;
|
2005-09-07 22:20:28 +00:00
|
|
|
inherited Create;
|
|
|
|
end;
|
|
|
|
|
|
|
|
destructor TSTLWrapper.Destroy;
|
|
|
|
begin
|
|
|
|
STL.Free;
|
|
|
|
inherited;
|
2005-08-26 18:29:39 +00:00
|
|
|
end;
|
|
|
|
|
2005-09-10 21:55:22 +00:00
|
|
|
destructor TStringWrapper.Destroy;
|
|
|
|
begin
|
|
|
|
Value := '';
|
|
|
|
inherited;
|
|
|
|
end;
|
|
|
|
|
2005-08-26 18:29:39 +00:00
|
|
|
initialization
|
|
|
|
|
|
|
|
FItems := TObjectList.Create;
|
|
|
|
|
|
|
|
finalization
|
|
|
|
|
|
|
|
FItems.Free;
|
|
|
|
|
|
|
|
end.
|