{ $Id$ } { /*************************************************************************** APIWizard.pp ------------------- APIWiZZ is an API wizard to generate WINAPI Templates for the LCL widgetsets. Initial Revision : 05-02-2000 ***************************************************************************/ /*************************************************************************** * * * This program 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. * * * ***************************************************************************/ } { @author(M. Weustink ) @created(02-May-2000) } unit APIWizard; {$mode objfpc}{$H+} interface uses Classes, SysUtils, Forms, Controls, Dialogs, StdCtrls, ExtCtrls, LCLproc, ComCtrls, LazFileUtils, LazUTF8, LazUTF8Classes; type { TApiWizForm } TApiWizForm = class(TForm) cmdCopy: TButton; cmdScan: TButton; cbgLCLPlatform: TCHECKGROUP; cmdGenerate: TButton; Groupbox1: TGROUPBOX; lvExisting: TListView; txtDeclarations: TMemo; rbIndependent: TRADIOBUTTON; rbDependent: TRADIOBUTTON; rdgApiType: TRadioGroup; lblDeclare: TLabel; txtLazarus: TEdit; lblLazarus: TLabel; procedure cmdCopyClick(Sender: TObject); procedure cmdGenerateClick(Sender: TObject); procedure ApiWizFormCreate(Sender: TObject); procedure cmdScanClick(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure rbPlatformDependencyClick(Sender: TObject); procedure rdgApiTypeClick (Sender: TObject ); private FLineInfo: TStringList; procedure Clear; public end; TProcType = (ptFunction, ptProcedure); TApiWidgetset = ( awCarbon, awCocoa, awFpgui, awGtk, awGtk2, awGtk3, awMUI, awNoGui, awQt, awWin32, awWinCE); TApiWidgetsets = set of TApiWidgetset; TApiLine = class Declaration: String; Independent: Boolean; WinApi: Boolean; Widgetsets: TApiWidgetsets; Base: Boolean; end; var ApiWizForm: TApiWizForm; implementation {$R apiwizard.lfm} const DECL_OFFSET: array[TProctype] of Integer = (9, 10); WS_NAME: array[TApiWidgetset] of string = ( 'carbon', 'cocoa', 'fpgui', 'gtk', 'gtk2', 'gtk3', 'mui', 'nogui', 'qt', 'win32', 'wince' ); function GetName(const ADeclaration: String): String; var n, NameEnd: Integer; begin Result := LowerCase(ADeclaration); n := Pos('function ', Result); if n <> 0 then Result := Trim(Copy(ADeclaration, n + DECL_OFFSET[ptFunction], Length(ADeclaration))) else begin n := Pos('procedure ', Result); if n <> 0 then Result := Trim(Copy(ADeclaration, n + DECL_OFFSET[ptProcedure], Length(ADeclaration))) else Result := ''; end; if Result = '' then Exit; NameEnd := Pos('(', Result); if NameEnd = 0 then NameEnd := Pos(':', Result); if NameEnd = 0 then NameEnd := Pos(';', Result); if NameEnd = 0 then NameEnd := Pos(' ', Result); n := Pos('.', Result); if (n > 0) and (((NameEnd > 0) and (n < NameEnd)) or (NameEnd = 0)) then begin Result := Trim(Copy(Result, n + 1, Length(Result))); if NameEnd > 0 then Dec(NameEnd, n); end; if NameEnd > 0 then Result := Trim(Copy(Result, 1, NameEnd - 1)); end; function SplitDeclaration(const ADeclaration: String; out ProcType: TProcType; out ProcName: String; const ProcParams: TStringList): Boolean; function ProcessParams(Params: String): Boolean; var n: Integer; S, part: String; begin Result := False; while Params <> '' do begin n := Pos(';', Params); if n = 0 then begin Part := Params; Params := ''; end else begin Part := Copy(Params, 1, n - 1); Params := Trim(Copy(Params, n + 1, Length(Params))); if Params = '' then Exit; end; n := Pos(':', Part); if n <> 0 then Part := Copy(Part, 1, n - 1); Part := Trim(Part); if (LowerCase(Copy(Part, 1, 4)) = 'var ') then Part := Trim(Copy(Part, 5, Length(Part))) else if (LowerCase(Copy(Part, 1, 4)) = 'out ') then Part := Trim(Copy(Part, 5, Length(Part))) else if (LowerCase(Copy(Part, 1, 6)) = 'const ') then Part := Trim(Copy(Part, 7, Length(Part))); repeat n := Pos(',', Part); if n = 0 then begin S := Part; Part := ''; end else begin S := Trim(Copy(Part, 1, n - 1)); Part := Trim(Copy(Part, n + 1, Length(Part))); end; if S = '' then Exit else ProcParams.Add(S); until Part = ''; end; Result := True; end; var ParamStart, ParamEnd, ParamLen: Integer; S: String; begin ProcParams.Clear; Result := False; ProcName := GetName(ADeclaration); if ProcName = '' then Exit; S := LowerCase(Trim(Copy(ADeclaration, 1, Pos(ProcName, ADeclaration) - 2))); if S = 'function' then ProcType := ptFunction else if S = 'procedure' then ProcType := ptProcedure else Exit; ParamStart := Pos('(', ADeclaration); ParamEnd := Pos(')', ADeclaration); if (ParamStart <> 0) and (ParamEnd <> 0) then ParamLen := ParamEnd - ParamStart - 1 else ParamLen := 0; if ((ParamStart <> 0) <> (ParamEnd <> 0)) or (ParamLen < 0) then Exit; if (ParamLen > 0) then Result := ProcessParams(Trim(Copy(ADeclaration, ParamStart + 1, ParamLen))) else Result := True; end; function FindPrevious(const Lines: TStringList; const AStart: Integer; const ATag, AFind: String): Integer; var StartTag: String; InsertFound: Boolean; begin InsertFound := False; StartTag := Format('##apiwiz##s%s##', [ATag]); for Result := AStart downto 0 do begin InsertFound := (Pos(AFind, Lines[Result]) <> 0) or (Pos(StartTag, Lines[Result]) <> 0); if InsertFound then Break; end; if InsertFound then Inc(Result) else Result := -1 end; function FindPreviousNonBlank(const Lines: TStringList; const AStart: Integer; const ATag: String): Integer; var StartTag: String; InsertFound: Boolean; begin InsertFound := False; StartTag := Format('##apiwiz##s%s##', [ATag]); for Result := AStart downto 0 do begin InsertFound := (Lines[Result] <> '') or (Pos(StartTag, Lines[Result]) <> 0); if InsertFound then Break; end; if InsertFound then Inc(Result) else Result := -1 end; function FindInsertPoint(const Lines: TStringList; const Tag, ProcName, FileName: String; const IsHeader: Boolean): Integer; var StartFound, InsertFound: Boolean; StartTag, EndTag: String; Line, SearchName, LineName: String; n: Integer; begin StartFound := False; InsertFound := False; StartTag := Format('##apiwiz##s%s##', [Tag]); EndTag := Format('##apiwiz##e%s##', [Tag]); SearchName := LowerCase(ProcName); for Result := 0 to Lines.Count - 1 do begin Line := Lines[Result]; if StartFound then begin if Pos(EndTag, Line) <> 0 then begin LineName := ''; InsertFound := True; Break; end; if (Line = '') or (Line[1] = ' ') then Continue; LineName := GetName(Line); if LineName = '' then Continue; n := CompareText(SearchName, LineName); if n = 0 then begin ShowMessageFmt('Function exists in [%s]', [FileName]); Break; end else if n < 0 then begin InsertFound := True; Break; end; end else begin StartFound := Pos(StartTag, Line) <> 0; Continue; end; end; if InsertFound then begin if IsHeader then begin if (LineName <> '') and (LowerCase(LineName[1]) <> LowerCase(Procname[1])) then Result := FindPreviousNonBlank(Lines, Result - 1, Tag); end else begin Result := FindPrevious(Lines, Result - 1, Tag, 'end;'); end; end else Result := -1 end; procedure InsertLines(const Idx: Integer; const TargetLines, SourceLines: TStringList); var n: Integer; begin for n := SourceLines.Count - 1 downto 0 do begin TargetLines.Insert(Idx, SourceLines[n]); end; end; procedure Scan(const AFileName, ATag: String; AResult: TStringList); var StartFound: Boolean; StartTag, EndTag: String; Line, LineName: String; Lines: TStringListUTF8; n: Integer; begin AResult.Clear; if not FileExistsUTF8(AFileName) then Exit; Lines := TStringListUTF8.Create; Lines.LoadFromFile(AFileName); StartFound := False; StartTag := Format('##apiwiz##s%s##', [ATag]); EndTag := Format('##apiwiz##e%s##', [ATag]); for n := 0 to Lines.Count - 1 do begin Line := Lines[n]; if Length(Line) < 8 then Continue; // min lenght of function,procedure,tag if not StartFound then begin StartFound := Pos(StartTag, Line) <> 0; Continue; end; if Pos(EndTag, Line) <> 0 then begin Break; end; if Line[1] = ' ' then Continue; if (Line[1] = '/') and (Line[2] = '/') then Continue; LineName := GetName(Line); if LineName = '' then Continue; Line := GetPart([], ['{$'], Line); AResult.Add(line); end; Lines.Free; end; { TApiWizForm } procedure TApiWizForm.ApiWizFormCreate(Sender: TObject); var n: Integer; S: String; WS: TApiWidgetset; C: TListColumn; begin cbgLCLPlatform.Items.Clear; for WS := Low(WS) to High(Ws) do cbgLCLPlatform.Items.Add(WS_NAME[WS]); lvExisting.Columns.BeginUpdate; for WS := Low(WS) to High(Ws) do begin C := lvExisting.Columns.Add; C.Caption := WS_NAME[WS]; C.Alignment := taCenter; C.AutoSize := True; end; // add dummy C := lvExisting.Columns.Add; C.Caption := ''; C.AutoSize := True; lvExisting.Columns.EndUpdate; FLineInfo := TStringList.Create; FLineInfo.Sorted := True; FLineInfo.Duplicates := dupIgnore; FLineInfo.CaseSensitive := False; S := ParamStrUTF8(1); // find the lazarus dir if S = '' then begin S := ExtractFilePath(ParamStrUTF8(0)); n := Pos('apiwizz', S); if n <> 0 then S := Copy(S, 1, n - 7) else S := ''; end; if S = '' then begin S := GetCurrentDirUTF8; n := Pos('apiwizz', S); if n <> 0 then S := Copy(S, 1, n - 7); end; txtLazarus.Text := S; end; procedure TApiWizForm.Clear; var n: Integer; begin lvExisting.Clear; for n := 0 to FLineInfo.Count - 1 do FLineInfo.Objects[n].Free; FLineInfo.Clear; end; procedure TApiWizForm.cmdCopyClick(Sender: TObject); var Line: TApiLine; WS: TApiWidgetset; begin if lvExisting.Selected = nil then Exit; Line := TApiLine(lvExisting.Selected.Data); if Line = nil then Exit; //??? if txtDeclarations.Lines.Count = 0 then begin txtDeclarations.Lines.Add(Line.Declaration); if Line.WinApi then rdgApiType.ItemIndex := 0 else rdgApiType.ItemIndex := 1; rbIndependent.Checked := Line.Independent; rbDependent.Checked := not Line.Independent; for WS := Low(WS) to High(Ws) do begin cbgLCLPlatform.Checked[Ord(WS)] := not(WS in Line.Widgetsets); end; Exit; end; // Check match if Line.WinApi <> (rdgApiType.ItemIndex = 0) then begin ShowMessage('API type mismatch'); Exit; end; if rbIndependent.Checked <> Line.Independent then begin ShowMessage('Dependency mismatch'); Exit; end; txtDeclarations.Lines.Add(Line.Declaration); for WS := Low(WS) to High(Ws) do begin if (WS in Line.Widgetsets) then Continue; cbgLCLPlatform.Checked[Ord(WS)] := True; end; end; procedure TApiWizForm.cmdGenerateClick(Sender: TObject); const RETURN_PROC: array[TProcType] of String = ('', ' Nothing'); PROC_DESC: array[TProcType] of String = ('Function', 'Procedure'); PROC_RESULT: array[TProcType] of String = ('Result := ', ''); var ApiText, ProcLines, ProcParams: TStringListUTF8; S, DeclarationText: String; ProcName, FileName, IntfBase: String; PlatformPrefix, PlatformDir, PlatformObject: String; Line, LineCount, n, Idx: Integer; ProcType: TProcType; WS: TApiWidgetset; procedure CreateLeadingCR; var LineName: String; begin // create leading CR if needed LineName := GetName(ApiText[Idx - 1]); if ((LineName <> '') and (LowerCase(ProcName)[1] <> LowerCase(LineName)[1])) or (Pos('##apiwiz##', ApiText[Idx]) <> 0) then ProcLines.Add(''); end; begin if txtDeclarations.Lines.Count = 0 then begin ShowMessage('No declaration specified'); Exit; end; case rdgApiType.ItemIndex of 0: begin FileName := 'winapi'; IntfBase := 'intfbasewinapi.inc'; end; 1: begin FileName := 'lclintf'; IntfBase := 'intfbaselcl.inc'; end; else ShowMessage('No API type selected'); Exit; end; LineCount := 0; ProcParams := TStringListUTF8.Create; ApiText := TStringListUTF8.Create; ProcLines := TStringListUTF8.Create; try for Line := 0 to txtDeclarations.Lines.Count - 1 do begin DeclarationText := Trim(txtDeclarations.Lines[Line]); while (Length(DeclarationText) > 0) and (DeclarationText[Length(DeclarationText)] in [';', ' ']) do begin Delete(DeclarationText, Length(DeclarationText), 1); end; if Length(DeclarationText) = 0 then Continue; if not SplitDeclaration(DeclarationText, ProcType, ProcName, ProcParams) then begin if MessageDlg(Format('Line %d, bad formatted declaration: %s', [Line + 1, DeclarationText]), mtError, [mbIgnore, mbAbort], 0) = mrIgnore then Continue; Exit; end; Inc(LineCount); //-------------------------------- // open winapih.inc / lclintfh.inc //-------------------------------- ApiText.LoadFromFile(txtLazarus.text + '/lcl/include/' + FileName + 'h.inc'); Idx := FindInsertPoint(ApiText, 'ps', ProcName, FileName + 'h.inc', True); if Idx <> -1 then begin ProcLines.Clear; CreateLeadingCR; if rbIndependent.Checked then ProcLines.Add(Format('//%s %s --> independent', [LowerCase(PROC_DESC[proctype]), procname])) else ProcLines.Add(DeclarationText + '; {$IFDEF IF_BASE_MEMBER}virtual;{$ENDIF}'); InsertLines(Idx, ApiText, ProcLines); if rbIndependent.Checked then begin Idx := FindInsertPoint(ApiText, 'pi', ProcName, FileName + 'h.inc', True); if Idx <> -1 then begin ProcLines.Clear; CreateLeadingCR; ProcLines.Add(DeclarationText + ';'); InsertLines(Idx, ApiText, ProcLines); end; end; ApiText.SaveToFile(txtLazarus.text + '/lcl/include/' + FileName + 'h.inc'); end; //------------------------------ // open winapi.inc / lclintf.inc //------------------------------ ApiText.LoadFromFile(txtLazarus.text + '/lcl/include/' + FileName + '.inc'); if rbIndependent.Checked then begin Idx := FindInsertPoint(ApiText, 'pi', ProcName, FileName + '.inc', False); if Idx <> -1 then begin with ProcLines do begin Clear; Add(''); Add('{------------------------------------------------------------------------------'); Add(' ' + PROC_DESC[ProcType] + ': ' + ProcName); if ProcParams.Count = 0 then Add(' Params: none' ) else begin Add(' Params: ' + ProcParams[0] + ':'); for n := 1 to ProcParams.Count - 1 do Add(' ' + ProcParams[n] + ':'); end; Add(' Returns:' + RETURN_PROC[ProcType]); Add(''); Add(' ------------------------------------------------------------------------------}'); Add(DeclarationText + ';'); Add('begin'); Add('//TODO:'+ProcName); Add(' // your code here'); Add('end;'); end; end; end else begin Idx := FindInsertPoint(ApiText, 'ps', ProcName, FileName + '.inc', False); if Idx <> -1 then begin if ProcParams.Count = 0 then S := '' else begin S := '(' + ProcParams[0]; for n := 1 to ProcParams.Count - 1 do S := S + ', ' + ProcParams[n]; S := S + ')' end; with ProcLines do begin Clear; Add(''); Add(DeclarationText + ';'); Add('begin'); Add(' ' + PROC_RESULT[ProcType] + 'WidgetSet.' + ProcName + S + ';'); Add('end;'); end; end; end; if Idx <> -1 then begin InsertLines(Idx, ApiText, ProcLines); ApiText.SaveToFile(txtLazarus.text + '/lcl/include/' + FileName + '.inc'); end; // ++++++++++++++++++++++++++++++ // // from here only dependent stuff // // ++++++++++++++++++++++++++++++ // if rbDependent.Checked then begin //------------------------------------------ // open intfbasewinapi.inc / intfbaselcl.inc //------------------------------------------ ApiText.LoadFromFile(txtLazarus.text + '/lcl/include/' + IntfBase); Idx := FindInsertPoint(ApiText, 'ps', ProcName, IntfBase, False); if Idx <> -1 then begin S := DeclarationText; // Remove spaces while S[DECL_OFFSET[ProcType]] = ' ' do Delete(S, DECL_OFFSET[ProcType], 1); System.Insert(' TWidgetSet.', S, DECL_OFFSET[ProcType]); with ProcLines do begin Clear; Add(''); Add(S + ';'); Add('begin'); if ProcType = ptFunction then begin Add(' // Your default here'); Add(' // Result := '); end; Add('end;'); end; InsertLines(Idx, ApiText, ProcLines); ApiText.SaveToFile(txtLazarus.text + '/lcl/include/' + IntfBase); end; for WS := Low(WS) to High(Ws) do begin if not cbgLCLPlatform.Checked[Ord(WS)] then Continue; PlatformPrefix := WS_NAME[WS]; PlatformDir := WS_NAME[WS]; PlatformObject := 'T' + WS_NAME[WS] + 'WidgetSet'; PlatformObject[2] := UpCase(PlatformObject[2]); //------------------ // open *winapih.inc //------------------ ApiText.LoadFromFile(txtLazarus.text + '/lcl/interfaces/' + PlatformDir + '/' + PlatformPrefix + FileName + 'h.inc'); Idx := FindInsertPoint(ApiText, 'ps', ProcName, PlatformPrefix + FileName + 'h.inc', True); if IDX <> -1 then begin ProcLines.Clear; CreateLeadingCR; ProcLines.Add(DeclarationText + '; override;'); InsertLines(Idx, ApiText, ProcLines); ApiText.SaveToFile(txtLazarus.text + '/lcl/interfaces/' + PlatformDir + '/' + PlatformPrefix + FileName + 'h.inc'); end; //----------------- // open *winapi.inc //----------------- ApiText.LoadFromFile(txtLazarus.text + '/lcl/interfaces/' + PlatformDir + '/' + PlatformPrefix + FileName + '.inc'); Idx := FindInsertPoint(ApiText, 'ps', ProcName, PlatformPrefix + FileName + '.inc', False); if Idx <> -1 then begin S := DeclarationText; // Remove spaces while S[DECL_OFFSET[ProcType]] = ' ' do Delete(S, DECL_OFFSET[ProcType], 1); System.Insert(' ' + PlatformObject + '.', S, DECL_OFFSET[ProcType]); with ProcLines do begin Clear; Add(''); Add('{------------------------------------------------------------------------------' ); Add(' ' + PROC_DESC[ProcType] + ': ' + ProcName ); if ProcParams.Count = 0 then Add(' Params: none' ) else begin Add(' Params: ' + ProcParams[0] + ':'); for n := 1 to ProcParams.Count - 1 do Add(' ' + ProcParams[n] + ':'); end; Add(' Returns:' + RETURN_PROC[ProcType] ); Add('' ); Add(' ------------------------------------------------------------------------------}' ); Add(S + ';'); Add('begin' ); Add(' // Your code here' ); Add('end;'); end; InsertLines(Idx, ApiText, ProcLines); ApiText.SaveToFile(txtLazarus.text + '/lcl/interfaces/' + PlatformDir + '/' + PlatformPrefix + FileName + '.inc'); end; end; end; end; finally ApiText.Free; ProcLines.Free; ProcParams.Free; ShowMessage(Format('%d lines inserted', [LineCount])); end; end; procedure TApiWizForm.cmdScanClick(Sender: TObject); var Lines: TStringlist; S: String; n, idx: Integer; Line: TApiLine; Item: TListItem; WS: TApiWidgetset; begin Screen.Cursor := crHourGlass; lvExisting.BeginUpdate; Clear; Lines := TStringList.Create; Scan(txtLazarus.text + '/lcl/include/winapih.inc', 'ps', Lines); for n := 0 to Lines.Count - 1 do begin Line := TApiLine.Create; Line.Declaration := Lines[n]; Line.WinApi := True; S := GetName(Lines[n]); FLineInfo.AddObject(S, Line); end; Scan(txtLazarus.text + '/lcl/include/winapih.inc', 'pi', Lines); for n := 0 to Lines.Count - 1 do begin S := GetName(Lines[n]); if FLineInfo.IndexOf(s) >= 0 then Continue; //overloaded Line := TApiLine.Create; Line.Independent := True; Line.WinApi := True; Line.Declaration := Lines[n]; FLineInfo.AddObject(S, Line); end; Scan(txtLazarus.text + '/lcl/include/lclintfh.inc', 'ps', Lines); for n := 0 to Lines.Count - 1 do begin Line := TApiLine.Create; Line.Declaration := Lines[n]; S := GetName(Lines[n]); FLineInfo.AddObject(S, Line); end; Scan(txtLazarus.text + '/lcl/include/lclintfh.inc', 'pi', Lines); for n := 0 to Lines.Count - 1 do begin S := GetName(Lines[n]); if FLineInfo.IndexOf(s) >= 0 then Continue; //overloaded Line := TApiLine.Create; Line.Independent := True; Line.Declaration := Lines[n]; FLineInfo.AddObject(S, Line); end; // implementations Scan(txtLazarus.text + '/lcl/include/intfbasewinapi.inc', 'ps', Lines); for n := 0 to Lines.Count - 1 do begin S := GetName(Lines[n]); idx := FLineInfo.IndexOf(S); if idx = -1 then Continue; Line := TApiLine(FLineInfo.Objects[idx]); Line.Base := True; end; Scan(txtLazarus.text + '/lcl/include/intfbaselcl.inc', 'ps', Lines); for n := 0 to Lines.Count - 1 do begin S := GetName(Lines[n]); idx := FLineInfo.IndexOf(S); if idx = -1 then Continue; Line := TApiLine(FLineInfo.Objects[idx]); Line.Base := True; end; for WS := Low(WS) to High(Ws) do begin Scan(txtLazarus.text + '/lcl/interfaces/' + WS_NAME[WS] + '/' + WS_NAME[WS] + 'winapih.inc', 'ps', Lines); for n := 0 to Lines.Count - 1 do begin S := GetName(Lines[n]); idx := FLineInfo.IndexOf(S); if idx = -1 then Continue; Line := TApiLine(FLineInfo.Objects[idx]); Include(Line.Widgetsets, WS); end; Scan(txtLazarus.text + '/lcl/interfaces/' + WS_NAME[WS] + '/' + WS_NAME[WS] + 'lclintfh.inc', 'ps', Lines); for n := 0 to Lines.Count - 1 do begin S := GetName(Lines[n]); idx := FLineInfo.IndexOf(S); if idx = -1 then Continue; Line := TApiLine(FLineInfo.Objects[idx]); Include(Line.Widgetsets, WS); end; end; for n := 0 to FLineInfo.Count - 1 do begin Item := lvExisting.Items.Add; Item.Caption := FLineInfo[n]; Line := TApiLine(FLineInfo.Objects[n]); Item.Data := Line; if Line.WinApi then Item.SubItems.Add('Win') else Item.SubItems.Add('LCL'); if Line.Independent then Item.SubItems.Add('X') else Item.SubItems.Add(''); if Line.Base then Item.SubItems.Add('X') else begin if Line.Independent then Item.SubItems.Add('-') else Item.SubItems.Add(''); end; for WS := Low(WS) to High(Ws) do begin if WS in Line.Widgetsets then Item.SubItems.Add('X') else begin if Line.Independent then Item.SubItems.Add('-') else Item.SubItems.Add(''); end; end; end; lvExisting.EndUpdate; Lines.Free; Screen.Cursor := crDefault; end; procedure TApiWizForm.FormDestroy(Sender: TObject); begin FreeAndNil(FLineInfo); end; procedure TApiWizForm.rbPlatformDependencyClick (Sender: TObject ); begin cbgLCLPlatform.Enabled := rbDependent.Checked; end; procedure TApiWizForm.rdgApiTypeClick (Sender: TObject ); begin end; end.