diff --git a/components/iphonelazext/ideext.pas b/components/iphonelazext/ideext.pas index cdfd906c3..9f1eb291d 100644 --- a/components/iphonelazext/ideext.pas +++ b/components/iphonelazext/ideext.pas @@ -147,8 +147,6 @@ begin finally xiblist.free; end; - - //todo: compile .xib files to .nibs end; function FindParam(const Source, ParamKey: String; var idx: Integer; var Content: String): Boolean; diff --git a/components/iphonelazext/iphonelazext.lpk b/components/iphonelazext/iphonelazext.lpk index 80bf3eccc..cfddb263a 100644 --- a/components/iphonelazext/iphonelazext.lpk +++ b/components/iphonelazext/iphonelazext.lpk @@ -18,7 +18,7 @@ - + @@ -72,6 +72,10 @@ + + + + diff --git a/components/iphonelazext/iphonelazext.pas b/components/iphonelazext/iphonelazext.pas index 923e0c897..7fe05fb72 100644 --- a/components/iphonelazext/iphonelazext.pas +++ b/components/iphonelazext/iphonelazext.pas @@ -9,7 +9,8 @@ interface uses ideext, iPhoneExtStr, iPhoneBundle, XCodeProject, environment_iphone_options, project_iphone_options, iPhoneExtOptions, - xcodetemplate, LazFilesUtils, XcodeUtils, newXibDialog, LazarusPackageIntf; + xcodetemplate, LazFilesUtils, XcodeUtils, newXibDialog, xibfile, + LazarusPackageIntf; implementation diff --git a/components/iphonelazext/newxibdialog.lfm b/components/iphonelazext/newxibdialog.lfm index 2dbf8446e..118de53fa 100644 --- a/components/iphonelazext/newxibdialog.lfm +++ b/components/iphonelazext/newxibdialog.lfm @@ -1,7 +1,7 @@ object newXibForm: TnewXibForm - Left = 583 + Left = 462 Height = 447 - Top = 195 + Top = 164 Width = 477 BorderIcons = [biSystemMenu] BorderStyle = bsDialog diff --git a/components/iphonelazext/project_iphone_options.lfm b/components/iphonelazext/project_iphone_options.lfm index f7c3f989e..2a281244b 100644 --- a/components/iphonelazext/project_iphone_options.lfm +++ b/components/iphonelazext/project_iphone_options.lfm @@ -4,8 +4,9 @@ inherited iPhoneProjectOptionsEditor: TiPhoneProjectOptionsEditor ClientHeight = 474 ClientWidth = 620 OnClick = FrameClick - DesignLeft = 463 - DesignTop = 306 + TabOrder = 0 + DesignLeft = 513 + DesignTop = 97 object chkisPhone: TCheckBox[0] Left = 16 Height = 18 @@ -180,11 +181,15 @@ inherited iPhoneProjectOptionsEditor: TiPhoneProjectOptionsEditor end object nibsPopup: TPopupMenu[17] OnPopup = nibsPopupPopup - left = 256 - top = 288 + left = 160 + top = 272 object mnuOpenIB: TMenuItem Caption = 'Open Interface Builder' OnClick = mnuOpenIBClick end + object mnuDump: TMenuItem + Caption = 'Dump classes to Pascal' + OnClick = mnuDumpClick + end end end diff --git a/components/iphonelazext/project_iphone_options.lrs b/components/iphonelazext/project_iphone_options.lrs index 8afaaad19..29658b182 100644 --- a/components/iphonelazext/project_iphone_options.lrs +++ b/components/iphonelazext/project_iphone_options.lrs @@ -3,23 +3,23 @@ LazarusResources.Add('TiPhoneProjectOptionsEditor','FORMDATA',[ 'TPF0'#241#27'TiPhoneProjectOptionsEditor'#26'iPhoneProjectOptionsEditor'#6'H' +'eight'#3#218#1#5'Width'#3'l'#2#12'ClientHeight'#3#218#1#11'ClientWidth'#3'l' - +#2#7'OnClick'#7#10'FrameClick'#10'DesignLeft'#3#207#1#9'DesignTop'#3'2'#1#0 - +#242#2#0#9'TCheckBox'#10'chkisPhone'#4'Left'#2#16#6'Height'#2#18#3'Top'#2#16 - +#5'Width'#3#199#0#7'Caption'#6#29'is iPhone application project'#8'OnChange' - +#7#16'chkisPhoneChange'#8'TabOrder'#2#0#0#0#242#2#1#6'TLabel'#8'lblAppID'#4 - +'Left'#2#16#6'Height'#2#18#3'Top'#2'X'#5'Width'#2'W'#7'Caption'#6#14'Applica' - +'tion ID'#11'ParentColor'#8#0#0#242#2#2#5'TEdit'#8'edtAppID'#4'Left'#2'p'#6 - +'Height'#2#22#3'Top'#2'U'#5'Width'#3#234#1#7'Anchors'#11#5'akTop'#6'akLeft'#7 - +'akRight'#0#8'TabOrder'#2#1#4'Text'#6#19'com.mycompany.myapp'#0#0#242#2#3#6 - +'TLabel'#12'lblAppIDHint'#4'Left'#2#16#6'Height'#2#14#3'Top'#2'u'#5'Width'#3 - +#237#1#7'Caption'#6'_It''s recommended by Apple to use domain-structured nam' - +'e, i.e. com.mycompany.myApplication as ID'#11'Font.Height'#2#246#11'ParentC' - +'olor'#8#10'ParentFont'#8#0#0#242#2#4#6'TLabel'#9'lblSDKVer'#4'Left'#2#16#6 - +'Height'#2#18#3'Top'#2'3'#5'Width'#2'P'#7'Caption'#6#12'SDK version:'#11'Par' - +'entColor'#8#0#0#242#2#5#9'TComboBox'#7'cmbSDKs'#4'Left'#2'p'#6'Height'#2#20 - +#3'Top'#2'0'#5'Width'#3#184#0#10'ItemHeight'#2#0#8'OnChange'#7#13'cmbSDKsCha' - +'nge'#5'Style'#7#14'csDropDownList'#8'TabOrder'#2#2#0#0#242#2#6#5'TEdit'#9'e' - +'dtResDir'#23'AnchorSideRight.Control'#7#15'btnShowInFinder'#4'Left'#2'x'#6 + +#2#7'OnClick'#7#10'FrameClick'#8'TabOrder'#2#0#10'DesignLeft'#3#1#2#9'Design' + +'Top'#2'a'#0#242#2#0#9'TCheckBox'#10'chkisPhone'#4'Left'#2#16#6'Height'#2#18 + +#3'Top'#2#16#5'Width'#3#199#0#7'Caption'#6#29'is iPhone application project' + +#8'OnChange'#7#16'chkisPhoneChange'#8'TabOrder'#2#0#0#0#242#2#1#6'TLabel'#8 + +'lblAppID'#4'Left'#2#16#6'Height'#2#18#3'Top'#2'X'#5'Width'#2'W'#7'Caption'#6 + +#14'Application ID'#11'ParentColor'#8#0#0#242#2#2#5'TEdit'#8'edtAppID'#4'Lef' + +'t'#2'p'#6'Height'#2#22#3'Top'#2'U'#5'Width'#3#234#1#7'Anchors'#11#5'akTop'#6 + +'akLeft'#7'akRight'#0#8'TabOrder'#2#1#4'Text'#6#19'com.mycompany.myapp'#0#0 + +#242#2#3#6'TLabel'#12'lblAppIDHint'#4'Left'#2#16#6'Height'#2#14#3'Top'#2'u'#5 + +'Width'#3#237#1#7'Caption'#6'_It''s recommended by Apple to use domain-struc' + +'tured name, i.e. com.mycompany.myApplication as ID'#11'Font.Height'#2#246#11 + +'ParentColor'#8#10'ParentFont'#8#0#0#242#2#4#6'TLabel'#9'lblSDKVer'#4'Left'#2 + +#16#6'Height'#2#18#3'Top'#2'3'#5'Width'#2'P'#7'Caption'#6#12'SDK version:'#11 + +'ParentColor'#8#0#0#242#2#5#9'TComboBox'#7'cmbSDKs'#4'Left'#2'p'#6'Height'#2 + +#20#3'Top'#2'0'#5'Width'#3#184#0#10'ItemHeight'#2#0#8'OnChange'#7#13'cmbSDKs' + +'Change'#5'Style'#7#14'csDropDownList'#8'TabOrder'#2#2#0#0#242#2#6#5'TEdit'#9 + +'edtResDir'#23'AnchorSideRight.Control'#7#15'btnShowInFinder'#4'Left'#2'x'#6 +'Height'#2#22#3'Top'#3#174#0#5'Width'#3'c'#1#7'Anchors'#11#5'akTop'#6'akLeft' +#7'akRight'#0#19'BorderSpacing.Right'#2#10#8'OnChange'#7#15'edtResDirChange' +#6'OnExit'#7#13'edtResDirExit'#8'TabOrder'#2#3#4'Text'#6#9'Resources'#0#0#242 @@ -54,7 +54,8 @@ LazarusResources.Add('TiPhoneProjectOptionsEditor','FORMDATA',[ +#14'btnAddXibClick'#8'TabOrder'#2#7#0#0#242#2#16#7'TButton'#12'btnRemoveXib' +#4'Left'#2'('#6'Height'#2#20#3'Top'#3'8'#1#5'Width'#2'J'#8'AutoSize'#9#7'Cap' +'tion'#6#6'Remove'#7'OnClick'#7#17'btnRemoveXibClick'#8'TabOrder'#2#8#0#0#242 - +#2#17#10'TPopupMenu'#9'nibsPopup'#7'OnPopup'#7#14'nibsPopupPopup'#4'left'#3#0 - +#1#3'top'#3' '#1#0#9'TMenuItem'#9'mnuOpenIB'#7'Caption'#6#22'Open Interface ' - +'Builder'#7'OnClick'#7#14'mnuOpenIBClick'#0#0#0#0 + +#2#17#10'TPopupMenu'#9'nibsPopup'#7'OnPopup'#7#14'nibsPopupPopup'#4'left'#3 + +#160#0#3'top'#3#16#1#0#9'TMenuItem'#9'mnuOpenIB'#7'Caption'#6#22'Open Interf' + +'ace Builder'#7'OnClick'#7#14'mnuOpenIBClick'#0#0#9'TMenuItem'#7'mnuDump'#7 + +'Caption'#6#22'Dump classes to Pascal'#7'OnClick'#7#12'mnuDumpClick'#0#0#0#0 ]); diff --git a/components/iphonelazext/project_iphone_options.pas b/components/iphonelazext/project_iphone_options.pas index f62c2a0f9..65e68740e 100644 --- a/components/iphonelazext/project_iphone_options.pas +++ b/components/iphonelazext/project_iphone_options.pas @@ -21,7 +21,7 @@ interface uses Classes,SysUtils,FileUtil,LResources,Forms,StdCtrls,CheckLst,Buttons, Dialogs, Menus,IDEOptionsIntf,ProjectIntf,LazIDEIntf,iPhoneExtStr, - iPhoneExtOptions, Controls, LazFilesUtils, XcodeUtils, newXibDialog; + iPhoneExtOptions, Controls, LazFilesUtils, XcodeUtils, newXibDialog, xibfile; type @@ -32,6 +32,7 @@ type btnAddXib:TButton; btnRemoveXib:TButton; Label5:TLabel; + mnuDump:TMenuItem; mnuOpenIB:TMenuItem; nibFilesBox:TCheckListBox; chkisPhone: TCheckBox; @@ -57,6 +58,7 @@ type procedure edtResDirChange(Sender:TObject); procedure edtResDirExit(Sender:TObject); procedure FrameClick(Sender: TObject); + procedure mnuDumpClick(Sender:TObject); procedure mnuOpenIBClick(Sender:TObject); procedure nibFilesBoxClickCheck(Sender:TObject); procedure nibFilesBoxItemClick(Sender:TObject;Index:integer); @@ -76,6 +78,7 @@ type procedure RefreshXIBList; procedure SetControlsEnabled(AEnabled: Boolean); + procedure DumpClasses(const XibFileName: AnsiString; var PascalFileName: AnsiString); public { public declarations } class function SupportedOptionsClass: TAbstractIDEOptionsClass; override; @@ -205,6 +208,25 @@ begin end; +procedure TiPhoneProjectOptionsEditor.mnuDumpClick(Sender:TObject); +var + s : AnsiString; + pas : AnsiString; + p : TControl; +begin + if SelXibFile ='' then Exit; + s:=ChangeFileExt(IncludeTrailingPathDelimiter (edtResDir.Text) + SelXibFile,'.xib'); + LazarusIDE.ActiveProject.LongenFilename(s); + DumpClasses(s, pas); + + p:=Parent; + while Assigned(p) and (not (p is TForm)) do + p:=p.Parent; + if Assigned(p) then TForm(p).ModalResult:=mrOK; + + LazarusIDE.DoOpenEditorFile(pas, -1,-1, [ofOnlyIfExists]); +end; + procedure TiPhoneProjectOptionsEditor.mnuOpenIBClick(Sender:TObject); var path : AnsiString; @@ -255,6 +277,7 @@ begin mnuOpenIB.Caption:=Format(strOpenXibAtIB, [SelXibFile]) else mnuOpenIB.Caption:=strOpenAtIB; + mnuDump.Enabled:=SelXibFile<>'' end; procedure TiPhoneProjectOptionsEditor.DoChanged; @@ -290,6 +313,181 @@ begin Controls[i].Enabled:=AEnabled; end; +function MethodName(const msg: AnsiString): String; +var + i : Integer; +begin + Result:=msg; + for i:=0 to length(Result) do if Result[i]=':' then Result[i]:='_'; +end; + +function ActionParams(const ActionName: AnsiString): String; +var + i : integer; + c : Integer; +begin + c:=0; + for i:=1 to length(ActionName) do if ActionName[i]=':' then inc(c); + case c of + 1 : Result:=('(sender: id)'); + 2 : Result:=('(sender: id; keyEvent: SEL)'); + end; +end; + +function SetMethodName(const propName: AnsiString): String; +begin + if propName<>'' then + Result:='set'+AnsiUpperCase(propName[1])+Copy(propName, 2, length(propName)-1) + else + Result:=''; +end; + +procedure XibClassToInterface(cls: TXibClassDescr; intf: TStrings); +var + i : Integer; +begin + if not Assigned(cls) or not Assigned(intf) then Exit; + + intf.Add(' { ' +cls.Name + ' }'); + intf.Add(''); + intf.Add(' ' +cls.Name + '=objcclass(NSObject)'); + if length(cls.Outlets)>0 then begin + intf.Add(' private'); + for i:=0 to length(cls.Outlets) - 1 do + intf.Add(' f'+cls.Outlets[i].Key+' : '+ cls.Outlets[i].Value+';'); + end; + + intf.Add(' public'); + for i:=0 to length(cls.Actions) - 1 do + with cls.Actions[i] do + intf.Add(Format(' function %s%s: %s; message ''%s'';', [MethodName(Key), ActionParams(Key), Value, Key])); + for i:=0 to length(cls.Outlets) - 1 do + with cls.Outlets[i] do begin + intf.Add(Format(' function %s: %s; message ''%s'';', [Key, Value, Key])); + intf.Add(Format(' procedure %s(a%s: %s); message ''%s'';', + [SetMethodName(Key), Key, Value, SetMethodName(Key)+':'])); + end; + if length(cls.Outlets) > 0 then + intf.Add(' procedure dealloc; override;'); + intf.Add(' end;'); + intf.Add(''); +end; + +procedure XibClassToImplementation(cls: TXibClassDescr; st: TStrings); +var + i : Integer; +begin + if not Assigned(cls) or not Assigned(st) then Exit; + if (length(cls.Actions)=0) and (length(cls.Outlets)=0) then exit; + st.Add(' { ' +cls.Name + ' }'); + st.Add(''); + for i:=0 to length(cls.Actions)-1 do + with cls.Actions[i] do begin + st.Add(Format('function %s.%s%s: %s;', [cls.Name, MethodName(Key), ActionParams(Key), Value])); + st.Add('begin'); + st.Add(' // put action''s code here'); + st.Add(' Result:=nil;'); + st.Add('end;'); + st.Add(''); + end; + + for i:=0 to length(cls.Outlets) - 1 do + with cls.Outlets[i] do begin + st.Add(Format('function %s.%s: %s;', [cls.Name, Key, Value])); + st.Add( 'begin'); + st.Add(Format(' Result:=f%s;', [Key])); + st.Add( 'end;'); + st.Add( ''); + st.Add(Format('procedure %s.%s(a%s: %s);', [cls.Name, SetMethodName(Key), Key, Value])); + st.Add( 'begin'); + st.Add(Format(' f%s:=a%s;', [Key, Key])); + st.Add(Format(' f%s.retain;', [Key])); + st.Add( 'end;'); + st.Add( ''); + end; + + if length(cls.Outlets)>0 then begin + st.Add(Format('procedure %s.dealloc; ', [cls.Name])); + st.Add( 'begin'); + for i:=0 to length(cls.Outlets) - 1 do + st.Add(Format(' f%s.release;',[cls.Outlets[i].Key])); + st.Add( ' inherited;'); + st.Add( 'end;'); + st.Add(''); + end; + +end; + +procedure TiPhoneProjectOptionsEditor.DumpClasses(const XibFileName: AnsiString; + var PascalFileName: AnsiString); +var + unitNm : AnsiString; + fs : TFileStream; + xibcls : TList; + i : Integer; + + intfPart : TStringList; + implPart : TStringList; + + cls : TXibClassDescr; + +const + le : Ansistring = LineEnding; + + procedure wln(const s: AnsiString); + begin + if s <>'' then + fs.Write(s[1], length(s)); + fs.Write(le[1], length(le)); + end; + +begin + if not FileExists(XibFileName) then Exit; + + intfPart:=TStringList.Create; + implPart:=TStringList.Create; + + xibcls:=TList.Create; + ListClassesDescr(XibFileName, xibcls); + + for i:=0 to xibcls.Count-1 do begin + cls:=TXibClassDescr(xibcls[i]); + XibClassToInterface(cls, intfPart); + XibClassToImplementation(cls, implPart); + cls.Free; + end; + xibcls.Free; + + unitNm:='dump'+ChangeFileExt(ExtractFileName(XibFileName),''); + PascalFileName:=unitNm+'.pas'; + LazarusIDE.ActiveProject.LongenFilename(PascalFileName); + + fs:=TFileStream.Create(PascalFileName, fmCreate); + + wln('unit ' + unitNm+';'); + wln(''); + wln('{$mode objfpc}{$h+}'); + wln('{$modeswitch objectivec1}'); + wln(''); + wln('interface'); + wln(''); + if intfPart.Count>0 then begin + wln('type'); + for i:=0 to intfPart.Count-1 do wln(intfPart[i]); + end; + wln(''); + wln('implementation'); + wln(''); + if implPart.Count>0 then + for i:=0 to implPart.Count-1 do wln(implPart[i]); + wln(''); + wln('end.'); + + intfPart.Free; + implPart.Free; + fs.Free; +end; + function TiPhoneProjectOptionsEditor.GetTitle: String; begin Result:=strPrjOptTitle; diff --git a/components/iphonelazext/xibfile.pas b/components/iphonelazext/xibfile.pas index 54702efe0..ffd7aed87 100644 --- a/components/iphonelazext/xibfile.pas +++ b/components/iphonelazext/xibfile.pas @@ -51,12 +51,60 @@ type procedure DoReadXibDoc(ADoc: TXMLDocument; var Obj: TXibObject); function FindXibObject(root: TXibObject; const ObjName: String; Recursive: Boolean=False): TXibObject; -procedure ListActionsAndOutlets(root: TXibObject; - actionsNames, actionsTypes: TStrings; - outletsNames, outletsTypes: TStrings); + +type + TXibKeyValue = record + Key : AnsiString; + Value : AnsiString; + end; + + { TXibClassDescr } + + TXibClassDescr = class(TObject) + Name : AnsiString; + Actions : array of TXibKeyValue; + Outlets : array of TXibKeyValue; + constructor Create(const AName: AnsiString); + end; + +procedure ListClassesDescr(root: TXibObject; DstList : TList); overload; +procedure ListClassesDescr(const FileName: AnsiString; DstList : TList); overload; implementation +function Min(a,b: integer): Integer; +begin + if a