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