diff --git a/components/iphonelazext/environment_iphone_options.lfm b/components/iphonelazext/environment_iphone_options.lfm
new file mode 100644
index 000000000..72fb76d23
--- /dev/null
+++ b/components/iphonelazext/environment_iphone_options.lfm
@@ -0,0 +1,130 @@
+inherited iPhoneSpecificOptions: TiPhoneSpecificOptions
+ Height = 439
+ Width = 485
+ ClientHeight = 439
+ ClientWidth = 485
+ DesignLeft = 631
+ DesignTop = 144
+ object lblRTLUtils: TLabel[0]
+ Left = 11
+ Height = 18
+ Top = 128
+ Width = 92
+ Caption = 'RTL units path'
+ ParentColor = False
+ end
+ object lblCompilerPath: TLabel[1]
+ Left = 11
+ Height = 18
+ Top = 96
+ Width = 55
+ Caption = 'Compiler'
+ ParentColor = False
+ end
+ object edtCompilerPath: TEdit[2]
+ Left = 125
+ Height = 22
+ Top = 96
+ Width = 347
+ Anchors = [akTop, akLeft, akRight]
+ TabOrder = 0
+ Text = 'edtCompilerPath'
+ end
+ object lblXCodeProject: TLabel[3]
+ Left = 11
+ Height = 18
+ Top = 72
+ Width = 395
+ Caption = 'Specific ARM compiler settings (for generated XCode projects)'
+ ParentColor = False
+ ParentFont = False
+ end
+ object edtRTLPath: TEdit[4]
+ Left = 125
+ Height = 22
+ Top = 128
+ Width = 347
+ Anchors = [akTop, akLeft, akRight]
+ TabOrder = 1
+ Text = 'edtRTLPath'
+ end
+ object edtCompilerOptions: TEdit[5]
+ Left = 125
+ Height = 22
+ Top = 160
+ Width = 347
+ Anchors = [akTop, akLeft, akRight]
+ TabOrder = 2
+ Text = 'edtCompilerOptions'
+ end
+ object lblCmpOptions: TLabel[6]
+ Left = 11
+ Height = 18
+ Top = 160
+ Width = 106
+ Caption = 'Compiler options'
+ ParentColor = False
+ ParentFont = False
+ OnClick = lblCmpOptionsClick
+ end
+ object Label5: TLabel[7]
+ Left = 11
+ Height = 18
+ Top = 16
+ Width = 97
+ Caption = 'Platforms path:'
+ ParentColor = False
+ ParentFont = False
+ end
+ object edtPlatformsPath: TEdit[8]
+ Left = 125
+ Height = 22
+ Top = 16
+ Width = 347
+ Anchors = [akTop, akLeft, akRight]
+ TabOrder = 3
+ Text = 'edtPlatformsPath'
+ end
+ object lblSimSettings: TLabel[9]
+ Left = 11
+ Height = 18
+ Top = 232
+ Width = 161
+ Caption = 'iPhone Simulator settings'
+ ParentColor = False
+ end
+ object edtSimBundle: TEdit[10]
+ Left = 160
+ Height = 22
+ Top = 262
+ Width = 312
+ Anchors = [akTop, akLeft, akRight]
+ TabOrder = 4
+ Text = 'edtSimBundle'
+ end
+ object edtSimApps: TEdit[11]
+ Left = 160
+ Height = 22
+ Top = 294
+ Width = 312
+ Anchors = [akTop, akLeft, akRight]
+ TabOrder = 5
+ Text = 'edtSimApps'
+ end
+ object lblSimBundle: TLabel[12]
+ Left = 11
+ Height = 18
+ Top = 263
+ Width = 111
+ Caption = 'iPhoneSim bundle'
+ ParentColor = False
+ end
+ object lblSimAppPath: TLabel[13]
+ Left = 11
+ Height = 18
+ Top = 294
+ Width = 141
+ Caption = 'Sim Applications path:'
+ ParentColor = False
+ end
+end
diff --git a/components/iphonelazext/environment_iphone_options.lrs b/components/iphonelazext/environment_iphone_options.lrs
new file mode 100644
index 000000000..04025482b
--- /dev/null
+++ b/components/iphonelazext/environment_iphone_options.lrs
@@ -0,0 +1,39 @@
+{ This is an automatically generated lazarus resource file }
+
+LazarusResources.Add('TiPhoneSpecificOptions','FORMDATA',[
+ 'TPF0'#241#22'TiPhoneSpecificOptions'#21'iPhoneSpecificOptions'#6'Height'#3
+ +#183#1#5'Width'#3#229#1#12'ClientHeight'#3#183#1#11'ClientWidth'#3#229#1#10
+ +'DesignLeft'#3'w'#2#9'DesignTop'#3#144#0#0#242#2#0#6'TLabel'#11'lblRTLUtils'
+ +#4'Left'#2#11#6'Height'#2#18#3'Top'#3#128#0#5'Width'#2'\'#7'Caption'#6#14'RT'
+ +'L units path'#11'ParentColor'#8#0#0#242#2#1#6'TLabel'#15'lblCompilerPath'#4
+ +'Left'#2#11#6'Height'#2#18#3'Top'#2'`'#5'Width'#2'7'#7'Caption'#6#8'Compiler'
+ +#11'ParentColor'#8#0#0#242#2#2#5'TEdit'#15'edtCompilerPath'#4'Left'#2'}'#6'H'
+ +'eight'#2#22#3'Top'#2'`'#5'Width'#3'['#1#7'Anchors'#11#5'akTop'#6'akLeft'#7
+ +'akRight'#0#8'TabOrder'#2#0#4'Text'#6#15'edtCompilerPath'#0#0#242#2#3#6'TLab'
+ +'el'#15'lblXCodeProject'#4'Left'#2#11#6'Height'#2#18#3'Top'#2'H'#5'Width'#3
+ +#139#1#7'Caption'#6'=Specific ARM compiler settings (for generated XCode pro'
+ +'jects)'#11'ParentColor'#8#10'ParentFont'#8#0#0#242#2#4#5'TEdit'#10'edtRTLPa'
+ +'th'#4'Left'#2'}'#6'Height'#2#22#3'Top'#3#128#0#5'Width'#3'['#1#7'Anchors'#11
+ +#5'akTop'#6'akLeft'#7'akRight'#0#8'TabOrder'#2#1#4'Text'#6#10'edtRTLPath'#0#0
+ +#242#2#5#5'TEdit'#18'edtCompilerOptions'#4'Left'#2'}'#6'Height'#2#22#3'Top'#3
+ +#160#0#5'Width'#3'['#1#7'Anchors'#11#5'akTop'#6'akLeft'#7'akRight'#0#8'TabOr'
+ +'der'#2#2#4'Text'#6#18'edtCompilerOptions'#0#0#242#2#6#6'TLabel'#13'lblCmpOp'
+ +'tions'#4'Left'#2#11#6'Height'#2#18#3'Top'#3#160#0#5'Width'#2'j'#7'Caption'#6
+ +#16'Compiler options'#11'ParentColor'#8#10'ParentFont'#8#7'OnClick'#7#18'lbl'
+ +'CmpOptionsClick'#0#0#242#2#7#6'TLabel'#6'Label5'#4'Left'#2#11#6'Height'#2#18
+ +#3'Top'#2#16#5'Width'#2'a'#7'Caption'#6#15'Platforms path:'#11'ParentColor'#8
+ +#10'ParentFont'#8#0#0#242#2#8#5'TEdit'#16'edtPlatformsPath'#4'Left'#2'}'#6'H'
+ +'eight'#2#22#3'Top'#2#16#5'Width'#3'['#1#7'Anchors'#11#5'akTop'#6'akLeft'#7
+ +'akRight'#0#8'TabOrder'#2#3#4'Text'#6#16'edtPlatformsPath'#0#0#242#2#9#6'TLa'
+ +'bel'#14'lblSimSettings'#4'Left'#2#11#6'Height'#2#18#3'Top'#3#232#0#5'Width'
+ +#3#161#0#7'Caption'#6#25'iPhone Simulator settings'#11'ParentColor'#8#0#0#242
+ +#2#10#5'TEdit'#12'edtSimBundle'#4'Left'#3#160#0#6'Height'#2#22#3'Top'#3#6#1#5
+ +'Width'#3'8'#1#7'Anchors'#11#5'akTop'#6'akLeft'#7'akRight'#0#8'TabOrder'#2#4
+ +#4'Text'#6#12'edtSimBundle'#0#0#242#2#11#5'TEdit'#10'edtSimApps'#4'Left'#3
+ +#160#0#6'Height'#2#22#3'Top'#3'&'#1#5'Width'#3'8'#1#7'Anchors'#11#5'akTop'#6
+ +'akLeft'#7'akRight'#0#8'TabOrder'#2#5#4'Text'#6#10'edtSimApps'#0#0#242#2#12#6
+ +'TLabel'#12'lblSimBundle'#4'Left'#2#11#6'Height'#2#18#3'Top'#3#7#1#5'Width'#2
+ +'o'#7'Caption'#6#16'iPhoneSim bundle'#11'ParentColor'#8#0#0#242#2#13#6'TLabe'
+ +'l'#13'lblSimAppPath'#4'Left'#2#11#6'Height'#2#18#3'Top'#3'&'#1#5'Width'#3
+ +#141#0#7'Caption'#6#22'Sim Applications path:'#11'ParentColor'#8#0#0#0
+]);
diff --git a/components/iphonelazext/environment_iphone_options.pas b/components/iphonelazext/environment_iphone_options.pas
new file mode 100644
index 000000000..42b0e87a8
--- /dev/null
+++ b/components/iphonelazext/environment_iphone_options.pas
@@ -0,0 +1,119 @@
+{
+ *****************************************************************************
+ * *
+ * This file is part of the iPhone Laz Extension *
+ * *
+ * See the file COPYING.modifiedLGPL.txt, included in this distribution, *
+ * for details about the copyright. *
+ * *
+ * This program 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. *
+ * *
+ *****************************************************************************
+}
+unit environment_iphone_options;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, FileUtil, LResources, Forms, StdCtrls,
+ IDEOptionsIntf, ProjectIntf,
+ iPhoneExtOptions;
+
+type
+
+ { TiPhoneSpecificOptions }
+
+ TiPhoneSpecificOptions = class(TAbstractIDEOptionsEditor)
+ edtCompilerPath: TEdit;
+ edtRTLPath: TEdit;
+ edtCompilerOptions: TEdit;
+ edtPlatformsPath: TEdit;
+ edtSimBundle: TEdit;
+ edtSimApps: TEdit;
+ lblRTLUtils: TLabel;
+ lblSimAppPath: TLabel;
+ lblCompilerPath: TLabel;
+ lblXCodeProject: TLabel;
+ lblCmpOptions: TLabel;
+ Label5: TLabel;
+ lblSimSettings: TLabel;
+ lblSimBundle: TLabel;
+ procedure lblCmpOptionsClick(Sender: TObject);
+ private
+ { private declarations }
+ public
+ { public declarations }
+ function GetTitle: String; override;
+ class function SupportedOptionsClass: TAbstractIDEOptionsClass; override;
+ procedure Setup(ADialog: TAbstractOptionsEditorDialog); override;
+ procedure ReadSettings(AOptions: TAbstractIDEOptions); override;
+ procedure WriteSettings(AOptions: TAbstractIDEOptions); override;
+ end;
+
+implementation
+
+{ TiPhoneSpecificOptions }
+
+procedure TiPhoneSpecificOptions.lblCmpOptionsClick(Sender: TObject);
+begin
+
+end;
+
+function TiPhoneSpecificOptions.GetTitle: String;
+begin
+ Result:='Files';
+end;
+
+procedure TiPhoneSpecificOptions.Setup(ADialog: TAbstractOptionsEditorDialog);
+begin
+
+end;
+
+procedure TiPhoneSpecificOptions.ReadSettings(AOptions: TAbstractIDEOptions);
+var
+ opt : TiPhoneEnvironmentOptions;
+begin
+ if not Assigned(AOptions) or not (AOptions is TiPhoneEnvironmentOptions) then Exit;
+ opt:=TiPhoneEnvironmentOptions(AOptions);
+ opt.Load;
+
+ edtPlatformsPath.Text := opt.PlatformsBaseDir;
+ edtCompilerPath.Text := opt.CompilerPath;
+ edtRTLPath.Text := opt.BaseRTLPath;
+ edtCompilerOptions.Text := opt.CommonOpt;
+ edtSimBundle.Text := opt.SimBundle;
+ edtSimApps.Text:= opt.SimAppsPath;
+end;
+
+procedure TiPhoneSpecificOptions.WriteSettings(AOptions: TAbstractIDEOptions);
+var
+ opt : TiPhoneEnvironmentOptions;
+begin
+ if not Assigned(AOPtions) or not (AOptions is TiPhoneEnvironmentOptions) then Exit;
+ opt:=TiPhoneEnvironmentOptions(AOptions);
+
+ opt.PlatformsBaseDir:=edtPlatformsPath.Text;
+ opt.CompilerPath:=edtCompilerPath.Text;
+ opt.BaseRTLPath:=edtRTLPath.Text;
+ opt.CommonOpt:=edtCompilerOptions.Text;
+ opt.SimBundle:=edtSimBundle.Text;
+ opt.SimAppsPath:=edtSimApps.Text;
+
+ opt.Save;
+end;
+
+class function TiPhoneSpecificOptions.SupportedOptionsClass: TAbstractIDEOptionsClass;
+begin
+ Result:=TiPhoneEnvironmentOptions;
+end;
+
+initialization
+ {$I environment_iphone_options.lrs}
+ RegisterIDEOptionsEditor(iPhoneEnvGroup, TiPhoneSpecificOptions, iPhoneEnvGroup+1);
+
+end.
+
diff --git a/components/iphonelazext/ideext.pas b/components/iphonelazext/ideext.pas
new file mode 100644
index 000000000..24f109ad3
--- /dev/null
+++ b/components/iphonelazext/ideext.pas
@@ -0,0 +1,442 @@
+{
+ *****************************************************************************
+ * *
+ * This file is part of the iPhone Laz Extension *
+ * *
+ * See the file COPYING.modifiedLGPL.txt, included in this distribution, *
+ * for details about the copyright. *
+ * *
+ * This program 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. *
+ * *
+ *****************************************************************************
+}
+unit ideext;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Unix, BaseUnix, process,
+ Classes, SysUtils,
+ Graphics, Controls, Forms, Dialogs, FileUtil,
+ {Lazarus Interface}
+ LazIDEIntf, MenuIntf, ProjectIntf, IDEOptionsIntf, PackageIntf, IDEMsgIntf,
+
+ project_iphone_options, xcodetemplate,
+
+ iPhoneExtOptions, iPhoneExtStr, iPhoneBundle, lazfilesutils;
+
+procedure Register;
+
+implementation
+
+type
+ { TiPhoneExtension }
+
+ TiPhoneExtension = class(TObject)
+ protected
+ procedure FillBunldeInfo(forSimulator: Boolean; var info: TiPhoneBundleInfo);
+ procedure InstallAppToSim;
+ function FixCustomOptions(const Options: String; isRealDevice: Boolean): String;
+ function ProjectBuilding(Sender: TObject): TModalResult;
+ function ProjectOpened(Sender: TObject; AProject: TLazProject): TModalResult;
+ function WriteIconTo(const FullName: String): Boolean;
+ public
+ isiPhoneMenu :TIDEMenuCommand;
+ constructor Create;
+ procedure UpdateXCode(Sender: TObject);
+ procedure SimRun(Sender: TObject);
+ procedure isProjectClicked(Sender: TObject);
+ end;
+
+var
+ Extension : TiPhoneExtension = nil;
+
+function GetProjectPlistName(Project: TLazProject): String;
+var
+ ext : String;
+begin
+ if Project.LazCompilerOptions.TargetFilename<>'' then
+ Result:=Project.LazCompilerOptions.TargetFilename
+ else begin
+ Result:=Project.MainFile.Filename;
+ ext:=ExtractFileExt(Result);
+ Result:=Copy(Result, 1, length(Result)-length( ext ) );
+ end;
+ Result:=Result+'.plist';
+end;
+
+function GetProjectExeName(Project: TLazProject): String;
+var
+ ext : String;
+begin
+ if Project.LazCompilerOptions.TargetFilename<>'' then
+ Result:=Project.LazCompilerOptions.TargetFilename
+ else begin
+ Result:=Project.MainFile.Filename;
+ ext:=ExtractFileExt(Result);
+ Result:=Copy(Result, 1, length(Result)-length( ext ) );
+ end;
+ Project.LongenFilename(Result);
+end;
+
+{ TiPhoneExtension }
+
+procedure TiPhoneExtension.FillBunldeInfo(forSimulator: Boolean; var info: TiPhoneBundleInfo);
+begin
+ Info.AppID:=ProjOptions.AppID;
+ Info.DisplayName:=LazarusIDE.ActiveProject.Title;
+ Info.iPlatform:=EnvOptions.GetSDKName(ProjOptions.SDK, forSimulator);
+ Info.SDKVersion:=ProjOptions.SDK;
+end;
+
+procedure TiPhoneExtension.InstallAppToSim;
+var
+ bundlename : string;
+ bundlepath : WideString;
+ exepath : WideString;
+ nm : String;
+ symlink : String;
+ Space : WideString;
+ RealSpace : WideString;
+ Info : TiPhoneBundleInfo;
+begin
+ Space:=ProjOptions.SpaceName; // LazarusIDE.ActiveProject.CustomData.;
+
+ {todo:}
+
+ bundleName:=ExtractFileName(LazarusIDE.ActiveProject.ProjectInfoFile);
+ bundleName:=Copy(bundleName, 1, length(bundleName)-length(ExtractFileExt(bundleName)));
+
+ nm:=GetProjectExeName(LazarusIDE.ActiveProject);
+
+ FillBunldeInfo(true, Info);
+ CreateBundle(bundleName, Space, ExtractFileName(nm), Info, RealSpace, bundlepath, exepath);
+
+ WriteIconTo( IncludeTrailingPathDelimiter(bundlepath)+'Icon.png');
+
+ if nm<>'' then begin
+ symlink:=UTF8Encode(exepath);
+ FpUnlink(symlink);
+ fpSymlink(PChar(nm), PChar(symlink));
+ end;
+end;
+
+function FindParam(const Source, ParamKey: String; var idx: Integer; var Content: String): Boolean;
+var
+ i : Integer;
+ quoted : Boolean;
+begin
+ Result:=false;
+ idx:=0;
+ for i := 1 to length(Source)-1 do
+ if (Source[i]='-') and ((i=1) or (Source[i-1] in [#9,#32,#10,#13])) then
+ if Copy(Source, i, 3) = ParamKey then begin
+ idx:=i;
+ Result:=true;
+ Break;
+ end;
+
+ if not Result then Exit;
+
+ i:=idx+3;
+ quoted:=(i<=length(Source)) and (Source[i]='"');
+
+ if not quoted then begin
+ for i:=i to length(Source) do
+ if (Source[i] in [#9,#32,#10,#13]) then begin
+ Content:=Copy(Source, idx+3, i-idx);
+ Exit;
+ end;
+ end else begin
+ i:=i+1;
+ for i:=i to length(Source) do
+ if (Source[i] = '"') then begin
+ Content:=Copy(Source, idx+3, i-idx+1);
+ Exit;
+ end;
+ end;
+ Content:=Copy(Source, idx+3, length(Source)-1);
+end;
+
+function TiPhoneExtension.FixCustomOptions(const Options: String; isRealDevice: Boolean): String;
+var
+ prm : string;
+ rawprm : string;
+ idx : Integer;
+ needfix : Boolean;
+ sdkuse : String;
+ sdkname : string;
+begin
+
+ if isRealDevice
+ then EnvOptions.GetDeviceSDK(ProjOptions.SDK, sdkname, sdkuse)
+ else EnvOptions.GetSimSDK(ProjOptions.SDK, sdkname, sdkuse);
+ sdkuse:=sdkuse;
+
+ Result:=Options;
+ if FindParam(Result, '-XR', idx, rawprm) then begin
+ if (rawprm='') or(rawprm[1]<>'"')
+ then prm:=rawprm
+ else prm:=rawprm;
+
+ // there might be -XR option, and it might be the same as selected SDK
+ needfix:=sdkuse<>prm;
+
+ // there's -XR option, but it doesn't match the selection options
+ if needfix then
+ Delete(Result, idx, length(rawprm)+3);
+
+ end else begin
+ //there's no -XR string in custom options
+ needfix:=true;
+ idx:=1;
+ sdkuse:=sdkuse+' ';
+ end;
+
+ if needfix then Insert('-XR'+sdkuse, Result, idx);
+end;
+
+constructor TiPhoneExtension.Create;
+begin
+ inherited Create;
+ LazarusIDE.AddHandlerOnProjectOpened(@ProjectOpened);
+ LazarusIDE.AddHandlerOnProjectBuilding(@ProjectBuilding);
+
+ RegisterIDEMenuCommand(itmProjectWindowSection, 'mnuiPhoneSeparator', '-', nil, nil);
+
+ isiPhoneMenu:=RegisterIDEMenuCommand(itmProjectWindowSection, 'mnuiPhoneProject', striPhoneProject, @isProjectClicked, nil);
+
+ RegisterIDEMenuCommand(itmProjectWindowSection, 'mnuiPhoneToXCode', strStartAtXCode, @UpdateXCode, nil);
+ RegisterIDEMenuCommand(itmProjectWindowSection, 'mnuiPhoneRunSim', strRunSimulator, @SimRun, nil);
+end;
+
+function TiPhoneExtension.ProjectBuilding(Sender: TObject): TModalResult;
+begin
+ //writeln('project building: ', Sender.ClassName);
+ Result:=mrOk;
+ if not Assigned(LazarusIDE.ActiveProject) or not ProjOptions.isIPhoneApp then Exit;
+
+ LazarusIDE.ActiveProject.LazCompilerOptions.CustomOptions :=
+ FixCustomOptions( LazarusIDE.ActiveProject.LazCompilerOptions.CustomOptions, false );
+
+ //const InfoFileName, BundleName, ExeName: WideString; const info: TiPhoneBundleInfo): Boolean;
+ {MessageDlg('CustomOptions fixed = '+ LazarusIDE.ActiveProject.LazCompilerOptions.CustomOptions,
+ mtInformation, [mbOK], 0);}
+
+ InstallAppToSim;
+ Result:=mrOk;
+end;
+
+function TiPhoneExtension.ProjectOpened(Sender: TObject; AProject: TLazProject): TModalResult;
+begin
+ ProjOptions.Reset;
+ ProjOptions.Load;
+ isiPhoneMenu.Checked:=ProjOptions.isIPhoneApp;
+ Result:=mrOk;
+end;
+
+function TiPhoneExtension.WriteIconTo(const FullName: String): Boolean;
+var
+ icofile : string;
+ ico : TIcon;
+ png : TPortableNetworkGraphic;
+ i, idx : Integer;
+const
+ iPhoneIconSize=57;
+begin
+ Result:=false;
+ //todo: find a better way of getting the file
+ icofile:=ChangeFileExt(LazarusIDE.ActiveProject.MainFile.Filename, '.ico');
+ LazarusIDE.ActiveProject.LongenFilename(icofile);
+ if not FileExists(icofile) then begin
+ // no icon. it should be deleted!
+ DeleteFile(FullName);
+ Exit;
+ end;
+
+ try
+ ico:=TIcon.Create;
+ png:=TPortableNetworkGraphic.Create;
+ try
+ png.Width:=iPhoneIconSize;
+ png.Height:=iPhoneIconSize;
+ ico.LoadFromFile(icofile);
+ idx:=-1;
+ for i:=0 to ico.Count- 1 do begin
+ ico.Current:=i;
+ if (ico.Width=iPhoneIconSize) and (ico.Height=iPhoneIconSize) then begin
+ idx:=i;
+ Break;
+ end;
+ end;
+ if (idx<0) and (ico.Count>0) then idx:=0;
+
+ ico.Current:=idx;
+
+ if (ico.Width=iPhoneIconSize) and (ico.Height=iPhoneIconSize) then
+ png.Assign(ico)
+ else begin
+ //resize to adjust the image
+ png.Canvas.CopyRect( Rect(0,0, iPhoneIconSize, iPhoneIconSize), ico.Canvas, Rect(0,0, ico.Width, ico.Height));
+ end;
+
+ png.SaveToFile(FullName);
+ finally
+ ico.free;
+ png.free;
+ end;
+ Result:=true;
+ except
+ end;
+end;
+
+procedure TiPhoneExtension.UpdateXCode(Sender: TObject);
+var
+ templates : TStringList;
+ build : TStringList;
+ dir : string;
+ projdir : string;
+ proj : TStringList;
+ projname : string;
+
+ ext : string;
+ tname : string;
+ plistname : string;
+
+ Info : TiPhoneBundleInfo;
+ name : string;
+
+ opt : string;
+begin
+ FillBunldeInfo(false, Info);
+ // the create .plist would be used by XCode project
+ // the simulator .plist in created with InstallAppToSim.
+ // they differ with SDKs used
+
+ build := TStringList.Create;
+
+ tname:=ExtractFileName( ChangeFileExt(LazarusIDE.ActiveProject.MainFile.Filename, ''));
+ plistname:=tname+'.plist';
+
+ build.Values['INFOPLIST_FILE'] := '"'+plistname+'"';
+ build.Values['PRODUCT_NAME'] := '"'+tname+'"';
+ build.Values['SDKROOT'] := EnvOptions.GetSDKName(ProjOptions.SDK, false);
+ build.Values['FPC_COMPILER_PATH'] := '"'+EnvOptions.CompilerPath+'"';
+ build.Values['FPC_MAIN_FILE'] := '"'+LazarusIDE.ActiveProject.MainFile.Filename+'"';
+ opt :=
+ ' -XR'+EnvOptions.GetSDKFullPath(ProjOptions.SDK, false)+' ' +
+ ' -FD'+IncludeTrailingPathDelimiter(EnvOptions.PlatformsBaseDir)+'iPhoneOS.platform/Developer/usr/bin ' +
+ EnvOptions.CommonOpt;
+
+ with LazarusIDE.ActiveProject.LazCompilerOptions do begin
+ opt:=opt + ' ' +BreakPathsStringToOption(OtherUnitFiles, '-Fu', '\"');
+ opt:=opt + ' ' +BreakPathsStringToOption(IncludePath, '-Fi', '\"');
+ opt:=opt + ' ' +BreakPathsStringToOption(ObjectPath, '-Fo', '\"');
+ opt:=opt + ' ' +BreakPathsStringToOption(Libraries, '-Fl', '\"');
+ end;
+
+ build.Values['FPC_CUSTOM_OPTIONS'] :='"'+opt+'"';
+
+ dir:='xcode';
+ LazarusIDE.ActiveProject.LongenFilename(dir);
+ dir:=dir+'/';
+
+ name:=ExtractFileName(GetProjectExeName(LazarusIDE.ActiveProject));
+ ForceDirectories(dir);
+ WriteDefInfoList( dir + GetProjectPlistName(LazarusIDE.ActiveProject),
+ name, name, Info);
+
+
+ projname:=ExtractFileName(LazarusIDE.ActiveProject.MainFile.Filename);
+ ext:=ExtractFileExt(projname);
+ projname:=Copy(projname, 1, length(projname)-length(ext));
+
+ projdir:=dir+projname+'.xcodeproj';
+ ForceDirectories(projdir);
+
+ projname:=IncludeTrailingPathDelimiter(projdir)+'project.pbxproj';
+ proj:=TStringList.Create;
+ templates:=nil;
+ try
+ if not FileExists(projname) then begin
+ templates:=TStringList.Create;
+
+ if WriteIconTo( IncludeTrailingPathDelimiter(dir)+'Icon.png') then begin
+ templates.Values['icon']:=XCodeProjectTemplateIcon;
+ templates.Values['iconid']:=XCodeProjectTemplateIconID;
+ templates.Values['iconfile']:=XCodeIconFile;
+ templates.Values['iconfileref']:=XCodeIconFileRef;
+ end else begin
+ templates.Values['icon']:='';
+ templates.Values['iconid']:='';
+ templates.Values['iconfile']:='';
+ templates.Values['iconfileref']:='';
+ end;
+
+ //todo:
+ templates.Values['bundle']:=tname+'.app';
+ templates.Values['plist']:=plistname;
+ templates.Values['targetname']:=tname;
+ templates.Values['productname']:=tname;
+
+ proj.Text:=XCodeProjectTemplate;
+ end else
+ proj.LoadFromFile(projname);
+
+ PrepareTemplateFile(proj, templates, build);
+ proj.SaveToFile(projname);
+ except
+ on e: exception do
+ ShowMessage(e.Message);
+ end;
+
+ proj.Free;
+ templates.Free;
+ build.Free;
+
+ IDEMessagesWindow.AddMsg(strXcodeUpdated, '', 0);
+end;
+
+procedure TiPhoneExtension.SimRun(Sender: TObject);
+var
+ t : TProcess;
+ path : String;
+begin
+ t :=TProcess.Create(nil);
+ try
+ path:=IncludeTrailingPathDelimiter(EnvOptions.SimBundle)+'Contents/MacOS/iPhone Simulator';
+ t.CommandLine:='"'+path+'"';
+ t.Execute;
+ except
+ on E: Exception do
+ MessageDlg(E.Message, mtInformation, [mbOK], 0);
+ end;
+ t.Free;
+end;
+
+procedure TiPhoneExtension.isProjectClicked(Sender: TObject);
+begin
+ if not Assigned(Sender) or not Assigned(LazarusIDE.ActiveProject) then Exit;
+ TIDEMenuCommand(Sender).Checked:=not TIDEMenuCommand(Sender).Checked;
+ ProjOptions.isIPhoneApp:=TIDEMenuCommand(Sender).Checked;
+ //SwitchProject(TIDEMenuCommand(Sender).Checked);
+end;
+
+procedure Register;
+begin
+ // IDE integration is done in constructor
+ Extension := TiPhoneExtension.Create;
+ EnvOptions.Load;
+end;
+
+initialization
+
+finalization
+ Extension.Free;
+
+end.
+
diff --git a/components/iphonelazext/ioptionsdialog.lfm b/components/iphonelazext/ioptionsdialog.lfm
new file mode 100644
index 000000000..9a254163d
--- /dev/null
+++ b/components/iphonelazext/ioptionsdialog.lfm
@@ -0,0 +1,98 @@
+object Form1: TForm1
+ Left = 301
+ Height = 431
+ Top = 268
+ Width = 629
+ Caption = 'iPhone options'
+ ClientHeight = 431
+ ClientWidth = 629
+ LCLVersion = '0.9.29'
+ object PageControl1: TPageControl
+ Left = 16
+ Height = 347
+ Top = 16
+ Width = 597
+ ActivePage = TabSheet2
+ Anchors = [akTop, akLeft, akRight, akBottom]
+ TabIndex = 1
+ TabOrder = 0
+ object TabSheet1: TTabSheet
+ Caption = 'iPhone Project Options'
+ end
+ object TabSheet2: TTabSheet
+ Caption = 'Global Options'
+ ClientHeight = 308
+ ClientWidth = 591
+ object Label1: TLabel
+ Left = 20
+ Height = 18
+ Top = 64
+ Width = 119
+ Alignment = taRightJustify
+ Caption = 'iPhone Sim Bundle:'
+ ParentColor = False
+ end
+ object Edit1: TEdit
+ Left = 149
+ Height = 22
+ Top = 60
+ Width = 416
+ Anchors = [akTop, akLeft, akRight]
+ TabOrder = 0
+ Text = 'Edit1'
+ end
+ object Label2: TLabel
+ Left = 37
+ Height = 18
+ Top = 116
+ Width = 102
+ Caption = 'FPC arm-darwin:'
+ ParentColor = False
+ end
+ object Edit2: TEdit
+ Left = 149
+ Height = 22
+ Top = 116
+ Width = 416
+ Anchors = [akTop, akLeft, akRight]
+ TabOrder = 1
+ Text = 'Edit2'
+ end
+ object Edit3: TEdit
+ Left = 149
+ Height = 22
+ Top = 28
+ Width = 416
+ TabOrder = 2
+ Text = 'Edit3'
+ end
+ object Label3: TLabel
+ Left = 9
+ Height = 18
+ Top = 28
+ Width = 130
+ Alignment = taRightJustify
+ Caption = 'iPhone Sim SDKs dir:'
+ ParentColor = False
+ end
+ end
+ end
+ object Button1: TButton
+ Left = 328
+ Height = 20
+ Top = 384
+ Width = 75
+ Anchors = [akLeft, akBottom]
+ Caption = 'Save'
+ TabOrder = 1
+ end
+ object Button2: TButton
+ Left = 232
+ Height = 20
+ Top = 384
+ Width = 75
+ Anchors = [akLeft, akBottom]
+ Caption = 'Cancel'
+ TabOrder = 2
+ end
+end
diff --git a/components/iphonelazext/ioptionsdialog.lrs b/components/iphonelazext/ioptionsdialog.lrs
new file mode 100644
index 000000000..600755647
--- /dev/null
+++ b/components/iphonelazext/ioptionsdialog.lrs
@@ -0,0 +1,29 @@
+{ This is an automatically generated lazarus resource file }
+
+LazarusResources.Add('TForm1','FORMDATA',[
+ 'TPF0'#6'TForm1'#5'Form1'#4'Left'#3'-'#1#6'Height'#3#175#1#3'Top'#3#12#1#5'Wi'
+ +'dth'#3'u'#2#7'Caption'#6#14'iPhone options'#12'ClientHeight'#3#175#1#11'Cli'
+ +'entWidth'#3'u'#2#10'LCLVersion'#6#6'0.9.29'#0#12'TPageControl'#12'PageContr'
+ +'ol1'#4'Left'#2#16#6'Height'#3'['#1#3'Top'#2#16#5'Width'#3'U'#2#10'ActivePag'
+ +'e'#7#9'TabSheet2'#7'Anchors'#11#5'akTop'#6'akLeft'#7'akRight'#8'akBottom'#0
+ +#8'TabIndex'#2#1#8'TabOrder'#2#0#0#9'TTabSheet'#9'TabSheet1'#7'Caption'#6#22
+ +'iPhone Project Options'#0#0#9'TTabSheet'#9'TabSheet2'#7'Caption'#6#14'Globa'
+ +'l Options'#12'ClientHeight'#3'4'#1#11'ClientWidth'#3'O'#2#0#6'TLabel'#6'Lab'
+ +'el1'#4'Left'#2#20#6'Height'#2#18#3'Top'#2'@'#5'Width'#2'w'#9'Alignment'#7#14
+ +'taRightJustify'#7'Caption'#6#18'iPhone Sim Bundle:'#11'ParentColor'#8#0#0#5
+ +'TEdit'#5'Edit1'#4'Left'#3#149#0#6'Height'#2#22#3'Top'#2'<'#5'Width'#3#160#1
+ +#7'Anchors'#11#5'akTop'#6'akLeft'#7'akRight'#0#8'TabOrder'#2#0#4'Text'#6#5'E'
+ +'dit1'#0#0#6'TLabel'#6'Label2'#4'Left'#2'%'#6'Height'#2#18#3'Top'#2't'#5'Wid'
+ +'th'#2'f'#7'Caption'#6#15'FPC arm-darwin:'#11'ParentColor'#8#0#0#5'TEdit'#5
+ +'Edit2'#4'Left'#3#149#0#6'Height'#2#22#3'Top'#2't'#5'Width'#3#160#1#7'Anchor'
+ +'s'#11#5'akTop'#6'akLeft'#7'akRight'#0#8'TabOrder'#2#1#4'Text'#6#5'Edit2'#0#0
+ +#5'TEdit'#5'Edit3'#4'Left'#3#149#0#6'Height'#2#22#3'Top'#2#28#5'Width'#3#160
+ +#1#8'TabOrder'#2#2#4'Text'#6#5'Edit3'#0#0#6'TLabel'#6'Label3'#4'Left'#2#9#6
+ +'Height'#2#18#3'Top'#2#28#5'Width'#3#130#0#9'Alignment'#7#14'taRightJustify'
+ +#7'Caption'#6#20'iPhone Sim SDKs dir:'#11'ParentColor'#8#0#0#0#0#7'TButton'#7
+ +'Button1'#4'Left'#3'H'#1#6'Height'#2#20#3'Top'#3#128#1#5'Width'#2'K'#7'Ancho'
+ +'rs'#11#6'akLeft'#8'akBottom'#0#7'Caption'#6#4'Save'#8'TabOrder'#2#1#0#0#7'T'
+ +'Button'#7'Button2'#4'Left'#3#232#0#6'Height'#2#20#3'Top'#3#128#1#5'Width'#2
+ +'K'#7'Anchors'#11#6'akLeft'#8'akBottom'#0#7'Caption'#6#6'Cancel'#8'TabOrder'
+ +#2#2#0#0#0
+]);
diff --git a/components/iphonelazext/ioptionsdialog.pas b/components/iphonelazext/ioptionsdialog.pas
new file mode 100644
index 000000000..f1bd40051
--- /dev/null
+++ b/components/iphonelazext/ioptionsdialog.pas
@@ -0,0 +1,7 @@
+unit iOptionsDialog;
+
+interface
+
+implementation
+
+end.
diff --git a/components/iphonelazext/iphonebundle.pas b/components/iphonelazext/iphonebundle.pas
new file mode 100644
index 000000000..c5f6242e8
--- /dev/null
+++ b/components/iphonelazext/iphonebundle.pas
@@ -0,0 +1,224 @@
+{
+ *****************************************************************************
+ * *
+ * This file is part of the iPhone Laz Extension *
+ * *
+ * See the file COPYING.modifiedLGPL.txt, included in this distribution, *
+ * for details about the copyright. *
+ * *
+ * This program 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. *
+ * *
+ *****************************************************************************
+}
+unit iPhoneBundle;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils;
+
+const
+ platform_iPhoneSim = 'iphonesimulator';
+ sdk_iPhoneSim2_0 = 'iphonesimulator2.0';
+ sdk_iPhoneSim2_1 = 'iphonesimulator2.1';
+ sdk_iPhoneSim2_2 = 'iphonesimulator2.2';
+ sdk_iPhoneSim3_0 = 'iphonesimulator3.0';
+ sdk_iPhoneSim3_1 = 'iphonesimulator3.1';
+
+type
+ TiPhoneBundleInfo = record
+ DisplayName : WideString; {if DisplayName='' then DisplayName=BundleName}
+ iPlatform : WideString;
+ SDKVersion : WideString;
+ AppID : WideString;
+ end;
+
+function GetUserHomeDir: WideString;
+
+function GetiPhoneSimUserPath(const UserHomeDir: WideString=''): WideString;
+
+procedure MakeSimSpaceStruct(const BundleName: WideString; var BundleAppDir: WideString);
+procedure MakeSimSpaceStruct(const iPhoneSimUserPath, BundleName: WideString; var BundleAppDir: WideString);
+procedure MakeSimSpaceStruct(const iPhoneSimUserPath, SpaceName, BundleName: WideString; var BundleAppDir: WideString);
+
+function GetBundleExeName(const BundleAppDir, ExeName: WideString): WideString;
+
+procedure WritePkgFile(const FileName: WideString);
+function WriteDefInfoList(const InfoFileName, BundleName, ExeName: WideString; const info: TiPhoneBundleInfo): Boolean;
+
+procedure CreateBundle(const BundleName, ExeName: WideString; const Info: TiPhoneBundleInfo; var FullBundlePath, FullExeName: WideString);
+procedure CreateBundle(const BundleName, SpaceName, ExeName: WideString; const Info: TiPhoneBundleInfo; var RealSpace, FullBundlePath, FullExeName: WideString);
+
+function AddPathDelim(const w: WideString): WideString;
+
+function RandomSpaceName: WideString;
+
+implementation
+
+function RandomSpaceName: WideString;
+var
+ g : TGUID;
+ id : String;
+begin
+ CreateGUID(g);
+ id:=GUIDToString(g);
+ id:=Copy(id, 2, length(id)-2);
+ Result:=id;
+end;
+
+
+procedure CreateBundle(const BundleName, SpaceName, ExeName: WideString; const Info: TiPhoneBundleInfo; var RealSpace, FullBundlePath, FullExeName: WideString);
+var
+ appdir : WideString;
+begin
+ if SpaceName='' then
+ RealSpace:=RandomSpaceName
+ else
+ RealSpace:=SpaceName;
+
+ MakeSimSpaceStruct(GetiPhoneSimUserPath, RealSpace, BundleName, appdir);
+ FullBundlePath:=appdir;
+ appdir:=AddPathDelim(appdir);
+ FullExeName:=appdir+ExeName;
+ WritePkgFile(appdir+'PkgInfo');
+ WriteDefInfoList(appdir+'Info.plist', BundleName, ExeName, Info);
+end;
+
+procedure CreateBundle(const BundleName, ExeName: WideString; const Info: TiPhoneBundleInfo; var FullBundlePath, FullExeName: WideString);
+var
+ sp : WideString;
+begin
+ CreateBundle(BundleName, '', ExeName, Info, sp, FullBundlePath, FullExeName);
+end;
+
+function AddPathDelim(const w: WideString): WideString;
+begin
+ if w='' then
+ Result:=PathDelim
+ else if w[length(w)]<>PathDelim then
+ Result:=w+PathDelim;
+end;
+
+function GetUserHomeDir: WideString;
+begin
+ Result:=UTF8Decode(GetUserDir);
+end;
+
+function GetiPhoneSimUserPath(const UserHomeDir: WideString=''): WideString;
+var
+ nm : WideString;
+begin
+ if UserHomeDir = '' then nm:=GetUserHomeDir
+ else nm:=UserHomeDir;
+
+ if nm='' then Exit;
+ if nm[length(nm)]<>'/' then nm:=nm+'/';
+ Result:=nm+'Library/Application Support/iPhone Simulator/User/Applications/';
+end;
+
+{
+~/Library/Application Support/iPhone Simulator/Users/%SPACENAME%
+
+%SPACENAME%/Applications
+%SPACENAME%/Applications/%AppBundle.app%
+%SPACENAME%/Documents
+%SPACENAME%/tmp
+}
+procedure MakeSimSpaceStruct(const iPhoneSimUserPath, SpaceName, BundleName: WideString; var BundleAppDir: WideString);
+var
+ path8 : String;
+ space8 : String;
+ p : string;
+begin
+ path8:=UTF8Encode(iPhoneSimUserPath);
+ space8:=UTF8Encode(SpaceName);
+
+ p:=IncludeTrailingPathDelimiter(IncludeTrailingPathDelimiter(path8)+space8);
+ BundleAppDir:=UTF8Decode(p+UTF8Encode(BundleName)+'.app');
+ ForceDirectories(UTF8Encode(BundleAppDir));
+ ForceDirectories(p+'Documents');
+ ForceDirectories(p+'tmp');
+
+end;
+
+procedure MakeSimSpaceStruct(const iPhoneSimUserPath, BundleName: WideString; var BundleAppDir: WideString);
+begin
+ MakeSimSpaceStruct(iPhoneSimUserPath, RandomSpaceName, BundleName, BundleAppDir);
+end;
+
+procedure MakeSimSpaceStruct(const BundleName: WideString; var BundleAppDir: WideString);
+begin
+ MakeSimSpaceStruct( GetiPhoneSimUserPath, BundleName, BundleAppDir);
+end;
+
+function GetBundleExeName(const BundleAppDir, ExeName: WideString): WideString;
+begin
+ Result:=AddPathDelim(BundleAppDir)+ExeName;
+end;
+
+procedure WritePkgFile(const FileName: WideString);
+var
+ fs : TFileStream;
+ s : String;
+begin
+ fs:=TFileStream.Create( UTF8Encode(FileName), fmCreate);
+ s:='APPL????';
+ fs.Write(s[1], length(s));
+ fs.Free;
+end;
+
+function WriteDefInfoList(const InfoFileName, BundleName, ExeName: WideString; const info: TiPhoneBundleInfo): Boolean;
+const
+ BundleFormat : AnsiString =
+ ''#10+
+ ''#10+
+ ''#10+
+ ''#10+
+ ' CFBundleDevelopmentRegion'#10+ ' English'#10+
+ ' CFBundleDisplayName'#10+ ' %s'#10+ {display name}
+ ' CFBundleExecutable'#10+ ' %s'#10+ {exe name}
+ ' CFBundleIdentifier'#10+ ' %s'#10+ {company + bundle name}
+ ' CFBundleInfoDictionaryVersion'#10+ ' 6.0'#10+
+ ' CFBundleName'#10+ ' %s'#10+ {bundle name}
+ ' CFBundlePackageType'#10+ ' APPL'#10+
+ ' CFBundleSignature'#10+ ' ????'#10+
+ ' CFBundleSupportedPlatforms'#10+ ' '#10+' %s'#10+' '#10+ {platform}
+ ' CFBundleVersion'#10+ ' 1.0'#10+
+ ' DTPlatformName'#10+ ' %s'#10+ {platform}
+ ' DTSDKName'#10+ ' %s'#10+ {sdk version}
+ ' LSRequiresIPhoneOS'#10+ ' '#10+
+ ''#10+
+ '';
+var
+ dispName : WideString;
+ s : String;
+ fs : TFileStream;
+begin
+ Result:=false;
+ if BundleName='' then Exit;
+
+ dispName:=info.DisplayName;
+ if dispName='' then dispName:=BundleName;
+
+ with info do
+ s:=Format( BundleFormat,
+ [ UTF8Encode(dispName),
+ UTF8Encode(ExeName),
+ UTF8Encode(AppID),
+ UTF8Encode(BundleName),
+ UTF8Encode(iPlatform),
+ UTF8Encode(iPlatform),
+ UTF8Encode(SDKVersion)
+ ]);
+ if FileExists(InfoFileName) then DeleteFile(InfoFileName);
+ fs:=TFileStream.Create(InfoFileName, fmCreate or fmOpenWrite);
+ if s<>'' then fs.Write(s[1], length(s));
+ fs.Free;
+end;
+
+end.
+
diff --git a/components/iphonelazext/iphoneextoptions.pas b/components/iphonelazext/iphoneextoptions.pas
new file mode 100644
index 000000000..fae2117f7
--- /dev/null
+++ b/components/iphonelazext/iphoneextoptions.pas
@@ -0,0 +1,331 @@
+{
+ *****************************************************************************
+ * *
+ * This file is part of the iPhone Laz Extension *
+ * *
+ * See the file COPYING.modifiedLGPL.txt, included in this distribution, *
+ * for details about the copyright. *
+ * *
+ * This program 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. *
+ * *
+ *****************************************************************************
+}
+
+unit iPhoneExtOptions;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, IDEOptionsIntf, LazIDEIntf, ProjectIntf, iPhoneBundle, XMLConf;
+
+type
+
+ { TiPhoneProjectOptions }
+
+ TiPhoneProjectOptions = class(TAbstractIDEProjectOptions)
+ private
+ fisiPhone : Boolean;
+ fAppID : string;
+ fSDK : string;
+ DataWritten : Boolean;
+ fSpaceName : string;
+ public
+ constructor Create;
+ class function GetGroupCaption: string; override;
+ class function GetInstance: TAbstractIDEOptions; override;
+ function Load: Boolean;
+ function Save: Boolean;
+ procedure Reset;
+ property isIPhoneApp: Boolean read fisIPhone write fisIPhone;
+ property SDK: string read fSDK write fSDK;
+ property AppID: string read fAppID write fAppID;
+ property SpaceName: string read fSpaceName write fSpaceName;
+ end;
+
+ { TiPhoneEnvironmentOptions }
+
+ TiPhoneEnvironmentOptions = class(TAbstractIDEEnvironmentOptions)
+ private
+ fPlatformsBaseDir : string;
+ fCompilerPath : string;
+ fBaseRTLPath : string;
+ fCommonOpt : string;
+ fSimAppsPath : string;
+ fSimBundle : string;
+ protected
+ function XMLFileName: string;
+ public
+ constructor Create;
+ class function GetGroupCaption: string; override;
+ class function GetInstance: TAbstractIDEOptions; override;
+
+ function Load: Boolean;
+ function Save: Boolean;
+
+ function GetSimSDK(const ProjSDK: string; var SDKName, SDKFullPath: string): Boolean;
+ function GetDeviceSDK(const ProjSDK: string; var SDKName, SDKFullPath: string): Boolean;
+
+ function GetSDKName(const SDKVer: string; simulator: Boolean): string;
+ function GetSDKFullPath(const SDKVer: string; simulator: Boolean): string;
+
+ procedure GetSDKVersions(Strings: TStringList);
+
+
+ property PlatformsBaseDir: string read fPlatformsBaseDir write fPlatformsBaseDir;
+ property CompilerPath: string read fCompilerPath write fCompilerPath;
+ property BaseRTLPath: string read fBaseRTLPath write fBaseRTLPath;
+ property CommonOpt: string read fCommonOpt write fCommonOpt;
+
+ property SimBundle: string read fSimBundle write fSimBundle;
+ property SimAppsPath: string read fSimAppsPath write fSimAppsPath;
+ end;
+
+function EnvOptions: TiPhoneEnvironmentOptions;
+function ProjOptions: TiPhoneProjectOptions;
+
+var
+ iPhoneEnvGroup : Integer;
+ iPhonePrjGroup : Integer;
+
+implementation
+
+var
+ fEnvOptions : TiPhoneEnvironmentOptions = nil;
+ fProjOptions : TiPhoneProjectOptions = nil;
+
+const
+ DefaultXMLName = 'iphoneextconfig.xml';
+
+ optisIphone = 'iPhone/isiPhoneApp';
+ optSDK = 'iPhone/SDK';
+ optAppID = 'iPhone/AppID';
+ optSpaceName = 'iPhone/SimSpaceName';
+
+
+function EnvOptions: TiPhoneEnvironmentOptions;
+begin
+ if not Assigned(fEnvOptions) then
+ fEnvOptions:=TiPhoneEnvironmentOptions.Create;
+ Result:=fEnvOptions;
+end;
+
+function ProjOptions: TiPhoneProjectOptions;
+begin
+ if not Assigned(fProjOptions) then
+ fProjOptions:=TiPhoneProjectOptions.Create;
+ Result:=fProjOptions;
+end;
+
+procedure InitOptions;
+begin
+ iPhoneEnvGroup := GetFreeIDEOptionsGroupIndex(GroupEnvironment);
+ iPhonePrjGroup := GetFreeIDEOptionsGroupIndex(GroupProject);
+ RegisterIDEOptionsGroup(iPhoneEnvGroup, TiPhoneEnvironmentOptions);
+ RegisterIDEOptionsGroup(iPhonePrjGroup, TiPhoneProjectOptions);
+end;
+
+procedure FreeOptions;
+begin
+ fEnvOptions.Free;
+end;
+
+{ TiPhoneEnvironmentOptions }
+
+class function TiPhoneEnvironmentOptions.GetGroupCaption: string;
+begin
+ Result:='iPhone Environment';
+end;
+
+class function TiPhoneEnvironmentOptions.GetInstance: TAbstractIDEOptions;
+begin
+ Result:=EnvOptions;
+end;
+
+function TiPhoneEnvironmentOptions.XMLFileName: String;
+begin
+ Result:=IncludeTrailingPathDelimiter(LazarusIDE.GetPrimaryConfigPath)+DefaultXMLName;
+end;
+
+function GetDefaultPlatformPath: WideString;
+begin
+ //todo:
+ Result:='/Developer/Platforms';
+end;
+
+function GetDefaultSimBundlePath: WideString;
+begin
+ //todo:
+ Result:=IncludeTrailingPathDelimiter(GetDefaultPlatformPath)+
+ 'iPhoneSimulator.platform/Developer/Applications/iPhone Simulator.app';
+end;
+
+function GetDefaultSimAppPath: WideSTring;
+begin
+ //todo:
+ Result:=IncludeTrailingPathDelimiter(GetUserDir)+
+ 'Library/Applications Support/iPhone Simulator/User/Applications/';
+end;
+
+constructor TiPhoneEnvironmentOptions.Create;
+begin
+ inherited Create;
+ fPlatformsBaseDir := GetDefaultPlatformPath;
+ fSimAppsPath := GetDefaultSimAppPath;
+ fSimBundle := GetDefaultSimBundlePath;
+ fCompilerPath := '/usr/local/bin/fpc';
+end;
+
+
+function TiPhoneEnvironmentOptions.Load: Boolean;
+var
+ xmlcfg : TXMLConfig;
+begin
+ Result:=true;
+ try
+ xmlcfg := TXMLConfig.Create(nil);
+ try
+ xmlcfg.RootName:='config';
+ xmlcfg.Filename:=XMLFileName;
+ fPlatformsBaseDir := UTF8Encode(xmlcfg.GetValue('Platforms', fPlatformsBaseDir ));
+ fCompilerPath := UTF8Encode(xmlcfg.GetValue('Compiler', fCompilerPath));
+ fBaseRTLPath := UTF8Encode(xmlcfg.GetValue('RTLPath', fBaseRTLPath));
+ fCommonOpt := UTF8Encode(xmlcfg.GetValue('CompilerOptions', fCommonOpt));
+ fSimBundle := UTF8Encode(xmlcfg.GetValue('SimBundle', fSimBundle));
+ fSimAppsPath := UTF8Encode(xmlcfg.GetValue('SimAppPath', fSimAppsPath));
+ finally
+ xmlcfg.Free;
+ end;
+ except
+ Result:=false;
+ end;
+end;
+
+function TiPhoneEnvironmentOptions.Save: Boolean;
+var
+ xmlcfg : TXMLConfig;
+begin
+ Result:=true;
+ try
+ xmlcfg := TXMLConfig.Create(nil);
+ try
+ xmlcfg.RootName:='config';
+ xmlcfg.Filename:=XMLFileName;
+ xmlcfg.SetValue('Platforms', UTF8Decode(fPlatformsBaseDir));
+ xmlcfg.SetValue('Compiler', UTF8Decode(fCompilerPath));
+ xmlcfg.SetValue('RTLPath', UTF8Decode(fBaseRTLPath));
+ xmlcfg.SetValue('CompilerOptions', UTF8Decode(fCommonOpt));
+ xmlcfg.SetValue('SimBundle', UTF8Decode(fSimBundle));
+ xmlcfg.SetValue('SimAppPath', UTF8Decode(fSimAppsPath));
+ finally
+ xmlcfg.Free;
+ end;
+ except
+ Result:=false;
+ end;
+
+end;
+
+function TiPhoneEnvironmentOptions.GetSimSDK(const ProjSDK: string; var SDKName, SDKFullPath: string): Boolean;
+const
+ iPhoneSimSDKDir = 'iPhoneSimulator2.0.sdk';
+begin
+ SDKName:=sdk_iPhoneSim2_0;
+ SDKFullPath:=IncludeTrailingPathDelimiter(fPlatformsBaseDir)+'iPhoneSimulator.platform/Developer/SDKs/'+iPhoneSimSDKDir;
+ Result:=true;
+end;
+
+function TiPhoneEnvironmentOptions.GetDeviceSDK(const ProjSDK: string; var SDKName, SDKFullPath: string): Boolean;
+begin
+ SDKName:='iphoneos2.0';
+ SDKFullPath:='';
+ Result:=false;
+end;
+
+function TiPhoneEnvironmentOptions.GetSDKName(const SDKVer: string;
+ simulator: Boolean): string;
+begin
+ if simulator then Result:='iphonesimulator2.0'
+ else Result:='iphoneos2.0';
+end;
+
+function TiPhoneEnvironmentOptions.GetSDKFullPath(const SDKVer: string; simulator: Boolean): string;
+begin
+ if simulator then
+ Result := IncludeTrailingPathDelimiter(fPlatformsBaseDir)+'iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator2.0.sdk'
+ else
+ Result := IncludeTrailingPathDelimiter(fPlatformsBaseDir)+'iPhoneOS.platform/Developer/SDKs/iPhoneOS2.0.sdk';
+end;
+
+procedure TiPhoneEnvironmentOptions.GetSDKVersions(Strings: TStringList);
+begin
+ //todo:
+ Strings.Add('iPhone OS 2.0');
+end;
+
+{ TiPhoneProjectOptions }
+
+procedure TiPhoneProjectOptions.Reset;
+begin
+ fisiPhone:=false;
+ fSDK:='iPhone 2.0';
+ fAppID:='com.mycompany.myapplication';
+ fSpaceName:='';
+ DataWritten:=false;
+end;
+
+constructor TiPhoneProjectOptions.Create;
+begin
+ inherited Create;
+ Reset;
+end;
+
+class function TiPhoneProjectOptions.GetGroupCaption: string;
+begin
+ Result:='iPhone';
+end;
+
+class function TiPhoneProjectOptions.GetInstance: TAbstractIDEOptions;
+begin
+ Result:=ProjOptions;
+end;
+
+function TiPhoneProjectOptions.Load: Boolean;
+begin
+ Result:=True;
+ with LazarusIDE.ActiveProject do begin
+ DataWritten:=CustomData.Contains(optisIphone);
+ fisiPhone:=(DataWritten) and (CustomData.Values[optisIphone] = 'true');
+ if CustomData.Contains(optSDK) then fSDK:=CustomData.Values[optSDK];
+ if CustomData.Contains(optAppID) then fAppID:=CustomData.Values[optAppID];
+ fSpaceName:=CustomData.Values[optSpaceName];
+ if fSpaceName='' then fSpaceName:=RandomSpaceName;
+ end;
+end;
+
+function TiPhoneProjectOptions.Save: Boolean;
+const
+ BoolStr : array[Boolean] of string = ('false', 'true');
+begin
+ Result:=True;
+ {do not write iPhone related info to non-iPhone projects}
+ if DataWritten or fisiPhone then
+ with LazarusIDE.ActiveProject do begin
+ CustomData.Values[optisIPhone] := BoolStr[fisiPhone];
+ CustomData.Values[optSDK]:=fSDK;
+ CustomData.Values[optAppID]:=fAppID;
+ CustomData.Values[optSpaceName]:=fSpaceName;
+ end;
+end;
+
+initialization
+ InitOptions;
+
+finalization
+ FreeOptions;
+
+end.
+
diff --git a/components/iphonelazext/iphoneextstr.pas b/components/iphonelazext/iphoneextstr.pas
new file mode 100644
index 000000000..06f89fed8
--- /dev/null
+++ b/components/iphonelazext/iphoneextstr.pas
@@ -0,0 +1,38 @@
+{
+ *****************************************************************************
+ * *
+ * This file is part of the iPhone Laz Extension *
+ * *
+ * See the file COPYING.modifiedLGPL.txt, included in this distribution, *
+ * for details about the copyright. *
+ * *
+ * This program 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. *
+ * *
+ *****************************************************************************
+}
+unit iPhoneExtStr;
+
+{$mode objfpc}{$H+}
+
+interface
+
+resourcestring
+ striPhoneProject = 'iPhone Project';
+ strStartAtXCode = 'Update XCode project';
+ strRunSimulator = 'Run iPhone Simulator';
+
+ strPrjOptTitle = 'iPhone specific';
+ strPrjOptIsiPhone = 'is iPhone application project';
+ strPrjOptSDKver = 'SDK version:';
+ strPrjOptCheckSDK = 'Check available SDKs';
+ strPtrOptAppID = 'Application ID';
+ strPtrOptAppIDHint = 'It''s recommended by Apple to use domain-structured identifier i.e. "com.mycompany.myApplication"';
+
+ strXcodeUpdated = 'Xcode project updated';
+
+implementation
+
+end.
+
diff --git a/components/iphonelazext/iphonelazext.lpk b/components/iphonelazext/iphonelazext.lpk
new file mode 100644
index 000000000..1f593d2a4
--- /dev/null
+++ b/components/iphonelazext/iphonelazext.lpk
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/iphonelazext/iphonelazext.pas b/components/iphonelazext/iphonelazext.pas
new file mode 100644
index 000000000..aa84b986c
--- /dev/null
+++ b/components/iphonelazext/iphonelazext.pas
@@ -0,0 +1,23 @@
+{ This file was automatically created by Lazarus. do not edit!
+ This source is only used to compile and install the package.
+ }
+
+unit iphonelazext;
+
+interface
+
+uses
+ ideext, iPhoneExtStr, iPhoneBundle, XCodeProject,
+ environment_iphone_options, project_iphone_options, iPhoneExtOptions,
+ xcodetemplate, lazfilesutils, LazarusPackageIntf;
+
+implementation
+
+procedure Register;
+begin
+ RegisterUnit('ideext', @ideext.Register);
+end;
+
+initialization
+ RegisterPackage('iphonelazext', @Register);
+end.
diff --git a/components/iphonelazext/lazfilesutils.pas b/components/iphonelazext/lazfilesutils.pas
new file mode 100644
index 000000000..599b6ff75
--- /dev/null
+++ b/components/iphonelazext/lazfilesutils.pas
@@ -0,0 +1,137 @@
+{
+ *****************************************************************************
+ * *
+ * This file is part of the iPhone Laz Extension *
+ * *
+ * See the file COPYING.modifiedLGPL.txt, included in this distribution, *
+ * for details about the copyright. *
+ * *
+ * This program 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. *
+ * *
+ *****************************************************************************
+}
+unit lazfilesutils;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, FileUtil,
+ LazIDEIntf,ProjectIntf;
+
+
+function ResolveProjectPath(const path: string; project: TLazProject = nil): string;
+
+function BreakPathsStringToOption(const Paths, Switch: String; const Quotes: string = '"'; project: TLazProject = nil): String;
+
+function RelativeToFullPath(const BasePath, Relative: string): String;
+function NeedQuotes(const path: string): Boolean;
+
+implementation
+
+function GetNextDir(const Path: string; var index: integer; var Name: string): Boolean;
+var
+ i : Integer;
+begin
+ Result:=index<=length(Path);
+ if not Result then Exit;
+
+ if Path[index]=PathDelim then inc(index);
+ Result:=index<=length(Path);
+ if not Result then Exit;
+
+ for i:=index to length(Path) do
+ if Path[i]=PathDelim then begin
+ Name:=Copy(Path, index, i - index);
+ index:=i+1;
+ Exit;
+ end;
+ Name:=Copy(Path, index, length(Path) - index+1);
+ index:=length(Path)+1;
+end;
+
+function RelativeToFullPath(const BasePath, Relative: string): String;
+var
+ i : integer;
+ nm : string;
+begin
+ Result:=ExcludeTrailingPathDelimiter(BasePath);
+ i:=1;
+ while GetNextDir(Relative, i, nm) do
+ if nm = '..' then
+ Result:=ExtractFileDir(Result)
+ else if nm <> '.' then
+ Result:=IncludeTrailingPathDelimiter(Result)+nm;
+end;
+
+function NeedQuotes(const path: string): Boolean;
+var
+ i : integer;
+const
+ SpaceChars = [#32,#9];
+begin
+ for i:=1 to length(path) do
+ if path[i] in SpaceChars then begin
+ Result:=true;
+ Exit;
+ end;
+ Result:=false;
+end;
+
+function QuoteStrIfNeeded(const path: string; const quotes: String): String;
+begin
+ if NeedQuotes(path) then
+ Result:=quotes+path+quotes
+ else
+ Result:=path;
+end;
+
+function ResolveProjectPath(const path: string; project: TLazProject): string;
+var
+ base : string;
+begin
+ if project=nil then project:=LazarusIDE.ActiveProject;
+
+ if FilenameIsAbsolute(Path) then
+ Result:=Path
+ else begin
+ base:='';
+ base:=ExtractFilePath(project.ProjectInfoFile);
+ Result:=RelativeToFullPath(base, Path);
+ end;
+end;
+
+function BreakPathsStringToOption(const Paths, Switch, Quotes: String; project: TLazProject): String;
+var
+ i, j : Integer;
+ fixed : String;
+begin
+ Result:='';
+ if not Assigned(project) then
+ project:=LazarusIDE.ActiveProject;
+
+ if not Assigned(project) then Exit;
+
+ j:=1;
+ for i:=1 to length(paths)-1 do
+ if Paths[i]=';' then begin
+ fixed:=Trim(Copy(paths,j, i-j) );
+ if fixed<>'' then begin
+ fixed:=ResolveProjectPath(fixed, project);
+ Result:=Result+' ' + Switch + QuoteStrIfNeeded(fixed, quotes);
+ end;
+ j:=i+1;
+ end;
+
+ fixed:=Trim(Copy(paths,j, length(paths)-j+1) );
+ if fixed<>'' then begin
+ fixed:=ResolveProjectPath(fixed, project);
+ Result:=Result+' ' + Switch + QuoteStrIfNeeded(fixed, quotes);
+ end;
+end;
+
+end.
+
diff --git a/components/iphonelazext/project_iphone_options.lfm b/components/iphonelazext/project_iphone_options.lfm
new file mode 100644
index 000000000..4aace2fdf
--- /dev/null
+++ b/components/iphonelazext/project_iphone_options.lfm
@@ -0,0 +1,90 @@
+inherited iPhoneProjectOptionsEditor: TiPhoneProjectOptionsEditor
+ Height = 374
+ Width = 618
+ ClientHeight = 374
+ ClientWidth = 618
+ OnClick = FrameClick
+ DesignLeft = 355
+ DesignTop = 224
+ object chkisPhone: TCheckBox[0]
+ Left = 16
+ Height = 18
+ Top = 16
+ Width = 199
+ Caption = 'is iPhone application project'
+ TabOrder = 0
+ end
+ object lblAppID: TLabel[1]
+ Left = 16
+ Height = 18
+ Top = 88
+ Width = 87
+ Caption = 'Application ID'
+ ParentColor = False
+ end
+ object edtAppID: TEdit[2]
+ Left = 112
+ Height = 22
+ Top = 85
+ Width = 488
+ Anchors = [akTop, akLeft, akRight]
+ TabOrder = 1
+ Text = 'com.mycompany.myapp'
+ end
+ object lblAppIDHint: TLabel[3]
+ Left = 16
+ Height = 14
+ Top = 117
+ Width = 493
+ Caption = 'It''s recommended by Apple to use domain-structured name, i.e. com.mycompany.myApplication as ID'
+ Font.Height = -10
+ ParentColor = False
+ ParentFont = False
+ end
+ object lblSDKVer: TLabel[4]
+ Left = 16
+ Height = 18
+ Top = 51
+ Width = 80
+ Caption = 'SDK version:'
+ ParentColor = False
+ end
+ object cmbSDKs: TComboBox[5]
+ Left = 112
+ Height = 20
+ Top = 48
+ Width = 184
+ ItemHeight = 0
+ ItemIndex = 0
+ Items.Strings = (
+ 'iPhone 2.0'
+ 'iPhone 2.1'
+ 'iPhone 2.2'
+ 'iPhone 2.2.1'
+ 'iPhone 3.0'
+ 'iPhone 3.1'
+ 'iPhone 3.1.2'
+ )
+ OnChange = cmbSDKsChange
+ Style = csDropDownList
+ TabOrder = 2
+ Text = 'iPhone 2.0'
+ end
+ object btnCheckSDK: TButton[6]
+ Left = 312
+ Height = 20
+ Top = 48
+ Width = 158
+ AutoSize = True
+ Caption = 'Check available SDKs'
+ TabOrder = 3
+ end
+ object lblXibFiles: TLabel[7]
+ Left = 16
+ Height = 18
+ Top = 168
+ Width = 219
+ Caption = '.XIB files might appear here soon...'
+ ParentColor = False
+ end
+end
diff --git a/components/iphonelazext/project_iphone_options.lrs b/components/iphonelazext/project_iphone_options.lrs
new file mode 100644
index 000000000..9dd87cbaf
--- /dev/null
+++ b/components/iphonelazext/project_iphone_options.lrs
@@ -0,0 +1,29 @@
+{ This is an automatically generated lazarus resource file }
+
+LazarusResources.Add('TiPhoneProjectOptionsEditor','FORMDATA',[
+ 'TPF0'#241#27'TiPhoneProjectOptionsEditor'#26'iPhoneProjectOptionsEditor'#6'H'
+ +'eight'#3'v'#1#5'Width'#3'j'#2#12'ClientHeight'#3'v'#1#11'ClientWidth'#3'j'#2
+ +#7'OnClick'#7#10'FrameClick'#10'DesignLeft'#3'c'#1#9'DesignTop'#3#224#0#0#242
+ +#2#0#9'TCheckBox'#10'chkisPhone'#4'Left'#2#16#6'Height'#2#18#3'Top'#2#16#5'W'
+ +'idth'#3#199#0#7'Caption'#6#29'is iPhone application project'#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'Left'#2'p'#6'Height'#2#22#3'Top'#2'U'#5'Width'#3#232#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 b'
+ +'y Apple to use domain-structured 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'cm'
+ +'bSDKs'#4'Left'#2'p'#6'Height'#2#20#3'Top'#2'0'#5'Width'#3#184#0#10'ItemHeig'
+ +'ht'#2#0#9'ItemIndex'#2#0#13'Items.Strings'#1#6#10'iPhone 2.0'#6#10'iPhone 2'
+ +'.1'#6#10'iPhone 2.2'#6#12'iPhone 2.2.1'#6#10'iPhone 3.0'#6#10'iPhone 3.1'#6
+ +#12'iPhone 3.1.2'#0#8'OnChange'#7#13'cmbSDKsChange'#5'Style'#7#14'csDropDown'
+ +'List'#8'TabOrder'#2#2#4'Text'#6#10'iPhone 2.0'#0#0#242#2#6#7'TButton'#11'bt'
+ +'nCheckSDK'#4'Left'#3'8'#1#6'Height'#2#20#3'Top'#2'0'#5'Width'#3#158#0#8'Aut'
+ +'oSize'#9#7'Caption'#6#20'Check available SDKs'#8'TabOrder'#2#3#0#0#242#2#7#6
+ +'TLabel'#11'lblXibFiles'#4'Left'#2#16#6'Height'#2#18#3'Top'#3#168#0#5'Width'
+ +#3#219#0#7'Caption'#6'$.XIB files might appear here soon...'#11'ParentColor'
+ +#8#0#0#0
+]);
diff --git a/components/iphonelazext/project_iphone_options.pas b/components/iphonelazext/project_iphone_options.pas
new file mode 100644
index 000000000..e3dba44a5
--- /dev/null
+++ b/components/iphonelazext/project_iphone_options.pas
@@ -0,0 +1,131 @@
+{
+ *****************************************************************************
+ * *
+ * This file is part of the iPhone Laz Extension *
+ * *
+ * See the file COPYING.modifiedLGPL.txt, included in this distribution, *
+ * for details about the copyright. *
+ * *
+ * This program 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. *
+ * *
+ *****************************************************************************
+}
+unit project_iphone_options;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, FileUtil, LResources, Forms, StdCtrls,
+ IDEOPtionsIntf, ProjectIntf,
+ iPhoneExtStr, iPhoneExtOptions;
+
+type
+
+ { TiPhoneProjectOptionsEditor }
+
+ TiPhoneProjectOptionsEditor = class(TAbstractIDEOptionsEditor)
+ btnCheckSDK: TButton;
+ chkisPhone: TCheckBox;
+ cmbSDKs: TComboBox;
+ edtAppID: TEdit;
+ lblXibFiles: TLabel;
+ lblAppID: TLabel;
+ lblAppIDHint: TLabel;
+ lblSDKVer: TLabel;
+ procedure cmbSDKsChange(Sender: TObject);
+ procedure FrameClick(Sender: TObject);
+ private
+ { private declarations }
+ public
+ { public declarations }
+ class function SupportedOptionsClass: TAbstractIDEOptionsClass; override;
+ function GetTitle: String; override;
+ procedure Setup(ADialog: TAbstractOptionsEditorDialog); override;
+ procedure ReadSettings(AOptions: TAbstractIDEOptions); override;
+ procedure WriteSettings(AOptions: TAbstractIDEOptions); override;
+ end;
+
+
+procedure EnableiPhoneAppProject(project: TLazProject);
+
+implementation
+
+procedure EnableiPhoneAppProject(project: TLazProject);
+begin
+
+end;
+
+{ TiPhoneProjectOptionsEditor }
+
+procedure TiPhoneProjectOptionsEditor.cmbSDKsChange(Sender: TObject);
+begin
+
+end;
+
+procedure TiPhoneProjectOptionsEditor.FrameClick(Sender: TObject);
+begin
+
+end;
+
+function TiPhoneProjectOptionsEditor.GetTitle: String;
+begin
+ Result:=strPrjOptTitle;
+end;
+
+procedure TiPhoneProjectOptionsEditor.Setup(ADialog: TAbstractOptionsEditorDialog);
+begin
+ chkisPhone.Caption := strPrjOptIsiPhone;
+ lblSDKVer.Caption := strPrjOptSDKver;
+ btnCheckSDK.Caption := strPrjOptCheckSDK;
+ lblAppID.Caption := strPtrOptAppID;
+ lblAppIDHint.Caption := strPtrOptAppIDHint;
+end;
+
+procedure TiPhoneProjectOptionsEditor.ReadSettings(AOptions: TAbstractIDEOptions);
+var
+ i : Integer;
+begin
+
+ with TiPhoneProjectOptions(AOptions) do
+ begin
+ Load;
+ chkisPhone.Checked:=isIPhoneApp;
+
+ i:=cmbSDKs.Items.IndexOf(SDK);
+ if (i<0) and (cmbSDKs.Items.Count>0) then i:=0;
+ cmbSDKs.ItemIndex:=i;
+
+ edtAppID.Text:=AppID;
+ end;
+
+end;
+
+procedure TiPhoneProjectOptionsEditor.WriteSettings(AOptions: TAbstractIDEOptions);
+begin
+ with TiPhoneProjectOptions(AOptions) do
+ begin
+ isIPhoneApp:=chkisPhone.Checked;
+ SDK:=cmbSDKs.Caption;
+ AppID:=edtAppID.Text;
+ Save;
+ end;
+end;
+
+class function TiPhoneProjectOptionsEditor.SupportedOptionsClass: TAbstractIDEOptionsClass;
+begin
+ Result:=TiPhoneProjectOptions;
+end;
+
+const
+ iPhoneOptions = 1;
+
+initialization
+ {$I project_iphone_options.lrs}
+ RegisterIDEOptionsEditor(iPhonePrjGroup, TiPhoneProjectOptionsEditor, iPhoneOptions);
+
+end.
+
diff --git a/components/iphonelazext/xcodeproject.pas b/components/iphonelazext/xcodeproject.pas
new file mode 100644
index 000000000..649c93983
--- /dev/null
+++ b/components/iphonelazext/xcodeproject.pas
@@ -0,0 +1,949 @@
+{
+ *****************************************************************************
+ * *
+ * This file is part of the iPhone Laz Extension *
+ * *
+ * See the file COPYING.modifiedLGPL.txt, included in this distribution, *
+ * for details about the copyright. *
+ * *
+ * This program 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. *
+ * *
+ *****************************************************************************
+}
+unit XCodeProject;
+
+{$mode objfpc}{$H+}
+// todo: re-write, using RTTI
+{$TYPEINFO ON}
+
+interface
+
+uses
+ contnrs, Classes, SysUtils, TypInfo;
+
+const
+ UTF8Mark = '// !$*UTF8*$!';
+
+ FileType_AppWrapper = 'wrapper.application';
+ FileType_Bundle = FileType_AppWrapper;
+ FileType_PList = 'text.plist.xml';
+ // sourceTree = BUILT_PRODUCTS_DIR
+
+type
+ TXCodeObjectID = string[24];
+
+ TStringValues = class(TStringList);
+
+ { TXCodeObject }
+ TXCodeObject = class(TObject)
+ private
+ fOwner : TXCodeObject;
+ fChild : TFPList;
+ fValues : TStringList;
+ fID : TXCodeObjectID;
+ protected
+ procedure AddObject(const AName: String; AObject: TXCodeObject);
+ function GetChild(i: Integer): TXCodeObject;
+ function GetCount: Integer;
+ procedure Clear;
+ function GetValue(const AName: String): String;
+ procedure SetValue(const AName: String; const AValue: String);
+ public
+ class function isa: ShortString; virtual; abstract;
+ constructor Create;
+ destructor Destroy; override;
+
+ property Child[i: integer]: TXCodeObject read GetChild;
+ property ChildCount: Integer read GetCount;
+ property Owner: TXCodeObject read fOwner;
+ property ID: TXCodeObjectID read fID;
+
+ property StrValue[const AName: String]: String read GetValue write SetValue;
+ function GetChildObject(const AName: String): TXCodeObject;
+ function GetObjectArray(const AName: String): TFPObjectList;
+ function GetStringArray(const AName: String): TStringList;
+ function GetStringValues(const AName: String): TStringValues;
+ end;
+ TXCodeObjectClass = class of TXCodeObject;
+
+ { TPBXFileReference }
+
+ TPBXFileReference = class(TXCodeObject)
+ private
+ function GetExplicitFileType: string;
+ function GetPath: String;
+ function GetSourceTree: String;
+ procedure SetExplicitFileType(const AValue: string);
+ procedure SetPath(const AValue: String);
+ procedure SetSourceTree(const AValue: String);
+ public
+ class function isa: ShortString; override;
+ property explicitFileType: string read GetExplicitFileType write SetExplicitFileType;
+ property path : String read GetPath write SetPath;
+ property sourceTree: String read GetSourceTree write SetSourceTree;
+ end;
+// 0A7E1DA610EAD478000D7855 /* iPhoneApp.app */ =
+
+ {isa = PBXFileReference;
+ explicitFileType = wrapper.application;
+ includeInIndex = 0;
+ path = iPhoneApp.app;
+ sourceTree = BUILT_PRODUCTS_DIR; }
+
+{isa = PBXFileReference;
+ lastKnownFileType = text.plist.xml;
+ path = "iPhoneApp-Info.plist"; sourceTree = ""; }
+
+
+
+ TXCodeBuildPhase = class(TXCodeObject);
+
+ { TPBXShellScriptBuildPhase }
+
+ TPBXShellScriptBuildPhase = class(TXCodeBuildPhase)
+ private
+ function GetBuildMask: Integer;
+ function GetShellPath: string;
+ function GetShellScript: String;
+ procedure SetBuildMask(const AValue: Integer);
+ procedure SetShellPath(const AValue: string);
+ procedure SetShellScript(const AValue: String);
+ public
+ constructor Create;
+ class function isa: ShortString; override;
+ property BuildActionMask : Integer read GetBuildMask write SetBuildMask;
+ property ShellPath : string read GetShellPath write SetShellPath;
+ property ShellScript : String read GetShellScript write SetShellScript;
+ end;
+
+ { TPBXBuildFile }
+
+ TPBXBuildFile = class(TXCodeObject)
+ public
+ fileRef : TPBXFileReference;
+ constructor Create;
+ destructor Destroy; override;
+ class function isa: ShortString; override;
+ end;
+
+ { TXCBuildConfiguration }
+
+ TXCBuildConfiguration = class(TXCodeObject)
+ private
+ function GetName: String;
+ procedure SetName(const AValue: String);
+ function GetBuildSettings: TStringValues;
+ public
+ class function isa: ShortString; override;
+ constructor Create;
+ destructor Destroy; override;
+ property name: String read GetName write SetName;
+ property Settings: TStringValues read GetBuildSettings;
+ end;
+
+ { TXCConfigurationList }
+
+ TXCConfigurationList = class(TXCodeObject)
+ private
+ fConfigs : TFPObjectList;
+ protected
+ function GetConfig(i: integer): TXCBuildConfiguration;
+ function GetCount: Integer;
+
+ function GetName: string;
+ procedure SetName(const AValue: string);
+ function GetVisible: Boolean;
+ procedure SetVisible(AVisible: Boolean);
+ public
+ class function isa: ShortString; override;
+ constructor Create;
+ destructor Destroy; override;
+ function AddList: TXCBuildConfiguration;
+ property Config[i: Integer] : TXCBuildConfiguration read GetConfig;
+ property ConfigCount: Integer read GetCount;
+ property defaultVisible: Boolean read GetVisible write SetVisible;
+ property defaulConfigName: string read GetName write SetName;
+ end;
+
+
+ { TPBXNativeTarget }
+
+ TPBXNativeTarget = class(TXCodeObject)
+ private
+ fConfigList : TXCConfigurationList;
+ fRef : TPBXFileReference;
+ function GetName: String;
+ function GetProductFile: TPBXFileReference;
+ function GetProductName: String;
+ function GetProductType: String;
+ procedure SetName(const AValue: String);
+ procedure SetProductName(const AValue: String);
+ procedure SetProductType(const AValue: String);
+
+ function GetConfigList: TXCConfigurationList;
+ public
+ constructor Create;
+ class function isa: ShortString; override;
+ function AddBuildConfig: TXCBuildConfiguration;
+ function AddScriptPhase: TPBXShellScriptBuildPhase;
+ property name: String read GetName write SetName;
+ property productName: String read GetProductName write SetProductName;
+ property productType: String read GetProductType write SetProductType;
+ property productFile: TPBXFileReference read GetProductFile;
+ end;
+
+ { TPBXProject }
+
+ TPBXProject = class(TXCodeObject)
+ private
+ fConfigList : TXCConfigurationList;
+ function GetDirPath: string;
+ function GetRoot: String;
+ procedure SetDirPath(const AValue: string);
+ procedure SetRoot(const AValue: String);
+ protected
+ function GetConfigList: TXCConfigurationList;
+ function GetTargets: TFPObjectList;
+
+ function GetVersion: String;
+ procedure SetVersion(const AVersion: string);
+ public
+ constructor Create;
+ class function isa: ShortString; override;
+ function AddTarget: TPBXNativeTarget;
+ function AddBuildConfig: TXCBuildConfiguration;
+ property DirPath: string read GetDirPath write SetDirPath;
+ property Root: String read GetRoot write SetRoot;
+ property compatibilityVersion: String read GetVersion write SetVersion;
+ end;
+
+ { TPBXGroup }
+
+ TPBXGroup = class(TXCodeObject)
+ protected
+ function GetPath: String;
+ procedure SetPath(const APath: String);
+ function GetSourceTree: String;
+ procedure SetSourceTree(const ATree: String);
+ public
+ class function isa: ShortString; override;
+ property Path: String read GetPath write SetPath;
+ property sourceTree: String read GetSourceTree write SetSourceTree;
+ end;
+
+procedure WritePBXProjFile(RootObject: TXCodeObject; Dst: TStream);
+procedure WritePBXProjFile(RootObject: TXCodeObject; const FileName: String);
+
+implementation
+
+const
+ BoolVal : array[Boolean] of String = ('false', 'true');
+
+function isQuotedStr(const s: string): Boolean;
+begin
+ Result:=(length(s)>=2) and (s[1] = '"') and (s[length(s)]='"');
+end;
+
+function QuotedStr(const s: string): String;
+begin
+ if isQuotedStr(s) then Result:=s
+ else Result:='"'+s+'"';
+end;
+
+function UnquoteStr(const s: string): String;
+begin
+ if not isQuotedStr(s) then Result:=s
+ else Result:=Copy(s, 2, length(s)-2);
+end;
+
+
+function GetBoolValue(xobj: TXCodeObject; const AName: String): boolean;
+begin
+ Result:=xobj.StrValue[AName]=BoolVal[True];
+end;
+
+procedure SetBoolValue(xobj: TXCodeObject; const AName: String; AValue: Boolean);
+begin
+ xobj.StrValue[AName]:=BoolVal[AValue];
+end;
+
+{ TXCConfigurationList }
+
+function TXCConfigurationList.GetName: string;
+begin
+ Result:=StrValue['defaultConfigurationName'];
+end;
+
+procedure TXCConfigurationList.SetName(const AValue: string);
+begin
+ StrValue['defaultConfigurationName']:=AValue;
+end;
+
+function TXCConfigurationList.GetVisible: Boolean;
+begin
+ Result:=GetBoolValue(Self, 'defaultConfigurationIsVisible');
+end;
+
+procedure TXCConfigurationList.SetVisible(AVisible: Boolean);
+begin
+ SetBoolValue(Self, 'defaultConfigurationIsVisible', AVisible);
+end;
+
+function TXCConfigurationList.GetConfig(i: integer): TXCBuildConfiguration;
+begin
+ Result:=TXCBuildConfiguration(fConfigs[i]);
+end;
+
+function TXCConfigurationList.GetCount: Integer;
+begin
+ Result:=fConfigs.Count;
+end;
+
+class function TXCConfigurationList.isa: ShortString;
+begin
+ Result:='XCConfigurationList';
+end;
+
+constructor TXCConfigurationList.Create;
+begin
+ inherited Create;
+ fConfigs:=GetObjectArray('buildConfigurations');
+end;
+
+destructor TXCConfigurationList.Destroy;
+begin
+ inherited Destroy;
+end;
+
+function TXCConfigurationList.AddList: TXCBuildConfiguration;
+begin
+ Result:=TXCBuildConfiguration.Create;
+ AddObject('', Result);
+ fConfigs.Add(Result);
+end;
+
+{ TXCBuildConfiguration }
+
+constructor TXCBuildConfiguration.Create;
+begin
+ inherited Create;
+ GetStringValues('buildSettings');
+end;
+
+destructor TXCBuildConfiguration.Destroy;
+begin
+ inherited Destroy;
+end;
+
+function TXCBuildConfiguration.GetName: String;
+begin
+ Result:=GetValue('name');
+end;
+
+procedure TXCBuildConfiguration.SetName(const AValue: String);
+begin
+ SetValue('name', AValue);
+end;
+
+function TXCBuildConfiguration.getBuildSettings: TStringValues;
+begin
+ Result:=GetStringValues('buildSettings');
+end;
+
+class function TXCBuildConfiguration.isa: ShortString;
+begin
+ Result:='XCBuildConfiguration';
+end;
+
+{ TPBXGroup }
+
+function TPBXGroup.GetPath: String;
+begin
+ Result:=StrValue['Path'];
+end;
+
+procedure TPBXGroup.SetPath(const APath: String);
+begin
+ StrValue['Path']:=APath;
+end;
+
+function TPBXGroup.GetSourceTree: String;
+begin
+ Result:=StrValue['sourceTree'];
+end;
+
+procedure TPBXGroup.SetSourceTree(const ATree: String);
+begin
+ StrValue['sourceTree']:=ATree;
+end;
+
+class function TPBXGroup.isa: ShortString;
+begin
+ Result:='PBXGroup';
+end;
+
+{ TPBXFileReference }
+
+class function TPBXFileReference.isa: ShortString;
+begin
+ Result:='PBXFileReference';
+end;
+
+function TPBXFileReference.GetExplicitFileType: string;
+begin
+ Result:=GetValue('explicitFileType');
+end;
+
+function TPBXFileReference.GetPath: String;
+begin
+ Result:=GetValue('path');
+end;
+
+function TPBXFileReference.GetSourceTree: String;
+begin
+ Result:=GetValue('sourceTree');
+end;
+
+procedure TPBXFileReference.SetExplicitFileType(const AValue: string);
+begin
+ SetValue('explicitFileType', AValue);
+end;
+
+procedure TPBXFileReference.SetPath(const AValue: String);
+begin
+ SetValue('path', AValue);
+end;
+
+procedure TPBXFileReference.SetSourceTree(const AValue: String);
+begin
+ SetValue('sourceTree', AValue);
+end;
+
+{ TPBXBuildFile }
+
+constructor TPBXBuildFile.Create;
+begin
+ fileRef:=TPBXFileReference.Create;
+end;
+
+destructor TPBXBuildFile.Destroy;
+begin
+ inherited Destroy;
+end;
+
+class function TPBXBuildFile.isa: ShortString;
+begin
+ Result:='PBXBuildFile';
+end;
+
+{ TXCodeObject }
+
+procedure TXCodeObject.AddObject(const AName: String; AObject: TXCodeObject);
+begin
+ AObject.fOwner:=Self;
+ fChild.Add(AObject);
+ if AName<>'' then
+ fValues.AddObject(AName, AObject);
+end;
+
+function TXCodeObject.GetChild(i: Integer): TXCodeObject;
+begin
+ Result:=TXCodeObject(fChild[i]);
+end;
+
+function TXCodeObject.GetCount: Integer;
+begin
+ Result:=fChild.Count;
+end;
+
+procedure TXCodeObject.Clear;
+var
+ i : integer;
+begin
+ for i:=0 to fValues.Count-1 do begin
+ if Assigned(fValues.Objects[i]) and not (fValues.Objects[i] is TXCodeObject) then
+ fValues.Objects[i].Free;
+ end;
+ fValues.Clear;
+
+ for i:=0 to fChild.Count-1 do
+ TObject(fChild[i]).Free;
+ fChild.Clear;
+end;
+
+function TXCodeObject.GetValue(const AName: String): String;
+begin
+ Result:=fValues.Values[AName];
+end;
+
+procedure TXCodeObject.SetValue(const AName, AValue: String);
+begin
+ fValues.Values[AName]:=Avalue;
+end;
+
+constructor TXCodeObject.Create;
+var
+ q : Qword;
+begin
+ inherited;
+ fChild:=TFPList.Create;
+ fValues:=TStringList.Create;
+ Q:=PtrUInt(Self);
+ fID:=hexStr(q, 24);
+end;
+
+destructor TXCodeObject.Destroy;
+begin
+ Clear;
+ fChild.Free;
+ fValues.Free;
+ inherited Destroy;
+end;
+
+function TXCodeObject.GetChildObject(const AName: String): TXCodeObject;
+var
+ i : integer;
+begin
+ Result:=nil;
+ i:=fValues.IndexOfName(AName);
+ if i>=0 then
+ if fValues.Objects[i] is TXCodeObject then
+ Result:=TXCodeObject(fValues.Objects[i]);
+end;
+
+function TXCodeObject.GetObjectArray(const AName: String): TFPObjectList;
+var
+ i : integer;
+begin
+ i:=fValues.IndexOfName(AName);
+ if i<0 then begin
+ Result:=TFPObjectList.Create(false);
+ fValues.AddObject(AName, Result);
+ end else begin
+ if not (fValues.Objects[i] is TFPObjectList) then
+ Result:=nil
+ else
+ Result:= TFPObjectList(fValues.Objects[i]);
+ end;
+end;
+
+function TXCodeObject.GetStringArray(const AName: String): TStringList;
+var
+ i : integer;
+begin
+ i:=fValues.IndexOfName(AName);
+ if i<0 then begin
+ Result:=TStringList.Create;
+ fValues.AddObject(AName, Result);
+ end else begin
+ if not (fValues.Objects[i] is TStringList) then
+ Result:=nil
+ else
+ Result:= TStringList(fValues.Objects[i]);
+ end;
+end;
+
+function TXCodeObject.GetStringValues(const AName: String): TStringValues;
+var
+ i : integer;
+begin
+ i:=fValues.IndexOfName(AName);
+ if i<0 then begin
+ Result:=TStringValues.Create;
+ fValues.Values[AName]:='?';
+ i:=fValues.IndexOfName(AName);
+ fValues.Objects[i]:=Result;
+ //i:=fValues.AddObject(AName + ' = ', Result);
+ end else begin
+ if not (fValues.Objects[i] is TStringValues) then
+ Result:=nil
+ else
+ Result:= TStringValues(fValues.Objects[i]);
+ end;
+end;
+
+type
+
+ { TPBXWriter }
+
+ TPBXWriter = class(TObject)
+ private
+ fDst : TStream;
+ fPrefix : String;
+ fIndent : String;
+ protected
+ procedure WriteItems(data:TObject;arg:pointer);
+ public
+ constructor Create(Dst: TStream);
+ destructor Destroy; override;
+ procedure Indent;
+ procedure Unindent;
+ procedure WriteLn(const utf8: AnsiString);
+ end;
+
+{ TPBXWriter }
+
+procedure TPBXWriter.WriteItems(data: TObject; arg: pointer);
+var
+ list : TFPList;
+ i : Integer;
+ j : Integer;
+ k : Integer;
+ xobj : TXCodeObject;
+ vobj : TObject;
+ sublist : TFPObjectList;
+ st : TStringList;
+ prefix : String;
+begin
+ list:=TFPList(data);
+ if not Assigned(list) or (list.Count=0) then Exit;
+
+ for i:=0 to list.Count-1 do begin
+ xobj:=TXCodeObject(list[i]);
+
+ Self.WriteLn( xobj.ID + ' = {');
+ Self.Indent;
+
+ Self.Writeln('isa = ' + xobj.isa+';');
+
+ for j:=0 to xobj.fValues.Count - 1 do begin
+ vobj:=xobj.fValues.Objects[j];
+ if not Assigned(vobj) then begin
+ prefix:=xobj.fValues.Names[j] + ' = ';
+ Writeln(prefix + xobj.fValues.ValueFromIndex[j]+';')
+ end else begin
+ if vobj is TXCodeObject then begin
+ prefix:=xobj.fValues[j] + ' = ';
+ Writeln(prefix + TXCodeObject(vobj).ID+';')
+ end else if vobj is TStringValues then begin
+ st:=TStringValues(vobj);
+ prefix:=xobj.fValues.Names[j]+' = ';
+ Self.Writeln(prefix + ' {');
+ Self.Indent;
+ for k:=0 to st.Count-1 do
+ Self.WritelN(st.Names[k] + ' = ' + st.ValueFromIndex[k] + ';' );
+ Self.Unindent;
+ Self.Writeln('};')
+ end else if vobj is TStringList then begin
+ prefix:=xobj.fValues[j] + ' = ';
+ st:=TStringValues(vobj);
+ Self.Writeln(prefix + ' (');
+ Self.Indent;
+ for k:=0 to st.Count-1 do
+ Self.WritelN(' ' + st[k] + ',' );
+ Self.Unindent;
+ Self.Writeln(');')
+ end else if vobj is TFPObjectList then begin
+ prefix:=xobj.fValues[j] + ' = ';
+ sublist:=TFPObjectList(vobj);
+ Self.Writeln(prefix + '(');
+ Self.Indent;
+ for k:=0 to sublist.Count-1 do
+ Self.WritelN(TXCodeObject(sublist[k]).ID+',');
+ Self.Unindent;
+ Self.Writeln(');')
+ end;
+ end;
+ end;
+
+ Self.Unindent;
+ Self.WritelN('};');
+ end;
+end;
+
+constructor TPBXWriter.Create(Dst: TStream);
+begin
+ inherited Create;
+ fDst:=Dst;
+ fIndent:=#9;
+end;
+
+destructor TPBXWriter.Destroy;
+begin
+ inherited Destroy;
+end;
+
+procedure TPBXWriter.Indent;
+begin
+ fPrefix:=fPrefix+fIndent;
+end;
+
+procedure TPBXWriter.Unindent;
+begin
+ fPrefix:=Copy(fPrefix,1, length(fPrefix)-length(fIndent));
+end;
+
+procedure TPBXWriter.WriteLn(const utf8: AnsiString);
+const
+ LF : string = #10;
+begin
+ if utf8='' then Exit;
+ if fPrefix<>'' then fDst.Write(fPrefix[1], length(fPrefix));
+ fDst.Write(utf8[1], length(utf8));
+ fDst.Write(LF[1], length(LF));
+end;
+
+procedure WritePBXProjFile(RootObject: TXCodeObject; Dst: TStream);
+var
+ lists : TFPHashObjectList; // list of TFPList
+ items : TFPList;
+ p : TXCodeObject;
+ stack : TFPList;
+ i : Integer;
+ xobjcount : Integer;
+ wr : TPBXWriter;
+begin
+ if RootObject=nil then Exit;
+
+ lists:=TFPHashObjectList.Create(True);
+
+ stack:=TFPList.Create;
+ xobjcount:=1;
+ stack.Add(RootObject);
+ while stack.Count > 0 do begin
+ p:= TXCodeObject(stack[0]);
+ items:=TFPList(lists.Find(p.isa));
+ if not Assigned(items) then begin
+ items:=TFPList.Create;
+ lists.Add(p.isa, items);
+ end;
+ items.Add(p);
+ inc(xobjcount, p.ChildCount);
+ for i:=0 to p.ChildCount-1 do stack.Add(p.Child[i]);
+ stack.Delete(0);
+ end;
+ stack.Free;
+
+ wr:=TPBXWriter.Create(Dst);
+ wr.WriteLn(UTF8Mark);
+ wr.WriteLn('{');
+ wr.Indent;
+ wr.WriteLn('archiveVersion = 1;');
+ wr.WriteLn('classes = {};');
+ wr.WriteLn('objectVersion = 45;');
+ wr.WriteLn('objects = { ');
+ wr.Indent;
+ lists.ForEachCall(@wr.WriteItems, nil);
+ wr.Unindent;
+ wr.WriteLn('};');
+ wr.WriteLn('rootObject = '+RootObject.ID+';');
+ wr.Unindent;
+ wr.WriteLn('}');
+ wr.Free;
+
+ lists.Free;
+end;
+
+procedure WritePBXProjFile(RootObject: TXCodeObject; const FileName: String);
+var
+ fs : TFileStream;
+begin
+ fs:=TFileStream.Create(FileName, fmCreate);
+ WritePBXProjFile(RootOBject, fs);
+ fs.Free;
+end;
+
+{ TXCProject }
+
+function TPBXProject.GetDirPath: string;
+begin
+ Result:=UnquoteStr(StrValue['projectDirPath']);
+end;
+
+function TPBXProject.GetRoot: String;
+begin
+ Result:=UnquoteStr(StrValue['projectRoot']);
+end;
+
+procedure TPBXProject.SetDirPath(const AValue: string);
+begin
+ StrValue['projectDirPath']:=QuotedStr(AValue);
+end;
+
+procedure TPBXProject.SetRoot(const AValue: String);
+begin
+ StrValue['projectRoot']:=QuotedStr(AValue);
+end;
+
+function TPBXProject.GetConfigList: TXCConfigurationList;
+begin
+ if not Assigned(fConfigList) then begin
+ fConfigList:=TXCConfigurationList.Create;
+ AddObject('buildConfigurationList', fConfigList);
+ end;
+ Result:=fConfigList;
+end;
+
+function TPBXProject.GetTargets: TFPObjectList;
+begin
+ Result:=GetObjectArray('targets');
+end;
+
+function TPBXProject.GetVersion: String;
+begin
+ Result:=GetValue('compatibilityVersion');
+end;
+
+procedure TPBXProject.SetVersion(const AVersion: string);
+begin
+ SetValue('compatibilityVersion', AVersion);
+end;
+
+constructor TPBXProject.Create;
+begin
+ inherited Create;
+ Root:='';
+ DirPath:='';
+end;
+
+class function TPBXProject.isa: ShortString;
+begin
+ Result:='PBXProject';
+end;
+
+function TPBXProject.AddTarget: TPBXNativeTarget;
+var
+ list : TFPObjectList;
+begin
+ // adding a target forces configuration list
+ //if fConfigList=nil then GetConfigList;
+
+ list:=GetObjectArray('targets');
+ Result:=TPBXNativeTarget.Create;
+ AddObject('', Result);
+ list.Add(Result);
+end;
+
+function TPBXProject.AddBuildConfig: TXCBuildConfiguration;
+begin
+ Result:=GetConfigList.AddList;
+end;
+
+{ TPBXNativeTarget }
+
+function TPBXNativeTarget.GetName: String;
+begin
+ Result:=GetValue('name');
+end;
+
+function TPBXNativeTarget.GetProductFile: TPBXFileReference;
+begin
+ if not Assigned(fRef) then begin
+ fRef:=TPBXFileReference.Create;
+ AddObject('productReference', fRef);
+ end;
+ Result:=fRef;
+end;
+
+procedure TPBXNativeTarget.SetName(const AValue: String);
+begin
+ SetValue('name', AValue);
+end;
+
+function TPBXNativeTarget.GetProductName: String;
+begin
+ Result:=GetValue('productName');
+end;
+
+procedure TPBXNativeTarget.SetProductName(const AValue: String);
+begin
+ SetValue('productName', AValue);
+end;
+
+function TPBXNativeTarget.GetProductType: String;
+begin
+ Result:=GetValue('productType');
+end;
+
+procedure TPBXNativeTarget.SetProductType(const AValue: String);
+begin
+ SetValue('productType', AValue)
+end;
+
+function TPBXNativeTarget.GetConfigList: TXCConfigurationList;
+begin
+ if not Assigned(fConfigList) then begin
+ fConfigList:=TXCConfigurationList.Create;
+ AddObject('buildConfigurationList', fConfigList);
+ end;
+ Result:=fConfigList;
+end;
+
+constructor TPBXNativeTarget.Create;
+begin
+ inherited Create;
+ productType := '"com.apple.product-type.application"';
+ GetObjectArray('buildPhases');
+ GetObjectArray('buildRules');
+ GetObjectArray('dependencies');
+end;
+
+class function TPBXNativeTarget.isa: ShortString;
+begin
+ Result:='PBXNativeTarget';
+end;
+
+function TPBXNativeTarget.AddBuildConfig: TXCBuildConfiguration;
+begin
+ Result:=GetConfigList.AddList;
+end;
+
+function TPBXNativeTarget.AddScriptPhase: TPBXShellScriptBuildPhase;
+begin
+ Result:=TPBXShellScriptBuildPhase.Create;
+ AddObject('', Result);
+ GetObjectArray('buildPhases').Add(Result);
+end;
+
+{ TPBXShellScriptBuildPhase }
+
+function TPBXShellScriptBuildPhase.GetBuildMask: Integer;
+begin
+ Result:=StrToIntDef( GetValue('buildActionMask'), 0);
+end;
+
+procedure TPBXShellScriptBuildPhase.SetBuildMask(const AValue: Integer);
+begin
+ SetValue('buildActionMask', IntToStr(AValue));
+end;
+
+function TPBXShellScriptBuildPhase.GetShellPath: string;
+begin
+ Result:=GetValue('shellPath');
+end;
+
+procedure TPBXShellScriptBuildPhase.SetShellPath(const AValue: string);
+begin
+ SetValue('shellPath', AValue);
+end;
+
+function TPBXShellScriptBuildPhase.GetShellScript: String;
+begin
+ Result:=UnquoteStr(GetValue('shellScript'));
+end;
+
+procedure TPBXShellScriptBuildPhase.SetShellScript(const AValue: String);
+begin
+ SetValue('shellScript', QuotedStr(AValue));
+end;
+
+constructor TPBXShellScriptBuildPhase.Create;
+begin
+ inherited Create;
+ ShellPath:='/bin/sh';
+ GetObjectArray('files');
+ GetObjectArray('inputPaths');
+ GetObjectArray('outputPaths');
+end;
+
+class function TPBXShellScriptBuildPhase.isa: ShortString;
+begin
+ Result:='PBXShellScriptBuildPhase';
+end;
+
+end.
+
+
+
diff --git a/components/iphonelazext/xcodetemplate.pas b/components/iphonelazext/xcodetemplate.pas
new file mode 100644
index 000000000..3c642d861
--- /dev/null
+++ b/components/iphonelazext/xcodetemplate.pas
@@ -0,0 +1,287 @@
+{
+ *****************************************************************************
+ * *
+ * This file is part of the iPhone Laz Extension *
+ * *
+ * See the file COPYING.modifiedLGPL.txt, included in this distribution, *
+ * for details about the copyright. *
+ * *
+ * This program 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. *
+ * *
+ *****************************************************************************
+}
+unit xcodetemplate;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, contnrs;
+
+procedure PrepareTemplateFile(Src, TemplateValues, BuildSettings: TStrings);
+
+const
+ XCodeProjectTemplateIconID : AnsiString ='0AE3FFA610F3C9AF00A9B007,';
+ XCodeProjectTemplateIcon : AnsiString =
+ '0AE3FFA610F3C9AF00A9B007 /* Icon.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = Icon.png; sourceTree = ""; };';
+
+ XCodeIconFile : AnsiString = '0A2C67AE10F3CFB800F48811,';
+ XCodeIconFileRef : AnsiString =
+ '0A2C67AE10F3CFB800F48811 /* Icon.png in Resources */ = {isa = PBXBuildFile; fileRef = 0AE3FFA610F3C9AF00A9B007 /* Icon.png */; };';
+
+
+ XCodeProjectTemplate : AnsiString =
+ '// !$*UTF8*$!'#10+
+ '{'#10+
+ ' archiveVersion = 1;'#10+
+ ' classes = {'#10+
+ ' };'#10+
+ ' objectVersion = 45;'#10+
+ ' objects = {'#10+
+ ' '#10+
+ ' ??iconfileref'#10+
+ ' '#10+
+ '/* Begin PBXFileReference section */'#10+
+ ' 0A85A8AE10F0D28700AB8400 /* ??bundle */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ??bundle; sourceTree = BUILT_PRODUCTS_DIR; };'#10+
+ ' 0A85A8B110F0D28700AB8400 /* ??plist */ = {isa = PBXFileReference; explicitFileType = text.plist.xml; name = ??plist; path = ??plist; sourceTree = SOURCE_ROOT; };'#10+
+ ' ??icon'#10+
+ '/* End PBXFileReference section */'#10+
+ ''#10+
+ '/* Begin PBXGroup section */'#10+
+ ' 0A52AE8110F0D05300478C4F = {'#10+
+ ' isa = PBXGroup;'#10+
+ ' children = ('#10+
+ ' ??iconid'#10+
+ ' 0A85A8AF10F0D28700AB8400 /* Products */,'#10+
+ ' 0A85A8B110F0D28700AB8400 /* ??plist */,'#10+
+ ' );'#10+
+ ' sourceTree = "";'#10+
+ ' };'#10+
+ ' 0A85A8AF10F0D28700AB8400 /* Products */ = {'#10+
+ ' isa = PBXGroup;'#10+
+ ' children = ('#10+
+ ' 0A85A8AE10F0D28700AB8400 /* ??bundle */,'#10+
+ ' );'#10+
+ ' name = Products;'#10+
+ ' sourceTree = "";'#10+
+ ' };'#10+
+ '/* End PBXGroup section */'#10+
+ ''#10+
+ '/* Begin PBXNativeTarget section */'#10+
+ ' 0A85A8AD10F0D28700AB8400 = {'#10+
+ ' isa = PBXNativeTarget;'#10+
+ ' buildConfigurationList = 0A85A8B410F0D28800AB8400 /* Build configuration list for PBXNativeTarget */;'#10+
+ ' buildPhases = ('#10+
+ ' 0A85A8B810F0D2D400AB8400 /* ShellScript */,'#10+
+ ' 0A2C67A610F3CEFA00F48811 /* Resources */,'#10+
+ ' );'#10+
+ ' buildRules = ('#10+
+ ' );'#10+
+ ' dependencies = ('#10+
+ ' );'#10+
+ ' name = ??targetname;'#10+
+ ' productName = ??productname;'#10+
+ ' productReference = 0A85A8AE10F0D28700AB8400 /* ??bundle */;'#10+
+ ' productType = "com.apple.product-type.application";'#10+
+ ' };'#10+
+ '/* End PBXNativeTarget section */'#10+
+ ''#10+
+ '/* Begin PBXProject section */'#10+
+ ' 0A52AE8310F0D05300478C4F /* Project object */ = {'#10+
+ ' isa = PBXProject;'#10+
+ ' buildConfigurationList = 0A52AE8610F0D05300478C4F /* Build configuration list for PBXProject "project1" */;'#10+
+ ' compatibilityVersion = "Xcode 3.1";'#10+
+ ' hasScannedForEncodings = 0;'#10+
+ ' mainGroup = 0A52AE8110F0D05300478C4F;'#10+
+ ' productRefGroup = 0A85A8AF10F0D28700AB8400 /* Products */;'#10+
+ ' projectDirPath = "";'#10+
+ ' projectRoot = "";'#10+
+ ' targets = ('#10+
+ ' 0A85A8AD10F0D28700AB8400,'#10+
+ ' );'#10+
+ ' };'#10+
+ '/* End PBXProject section */'#10+
+ ''#10+
+ '/* Begin PBXResourcesBuildPhase section */'#10+
+ ' 0A2C67A610F3CEFA00F48811 /* Resources */ = {'#10+
+ ' isa = PBXResourcesBuildPhase;'#10+
+ ' buildActionMask = 2147483647;'#10+
+ ' files = ('#10+
+ ' ??iconfile'#10+
+ ' );'#10+
+ ' runOnlyForDeploymentPostprocessing = 0;'#10+
+ ' };'#10+
+ '/* End PBXResourcesBuildPhase section */'#10+
+ ''#10+
+ '/* Begin PBXShellScriptBuildPhase section */'#10+
+ ' 0A85A8B810F0D2D400AB8400 /* ShellScript */ = {'#10+
+ ' isa = PBXShellScriptBuildPhase;'#10+
+ ' buildActionMask = 2147483647;'#10+
+ ' files = ('#10+
+ ' );'#10+
+ ' inputPaths = ('#10+
+ ' );'#10+
+ ' outputPaths = ('#10+
+ ' );'#10+
+ ' runOnlyForDeploymentPostprocessing = 0;'#10+
+ ' shellPath = /bin/sh;'#10+
+ ' shellScript = "if [ x\"$ACTION\" != \"xbuild\" ]; then\n # in case running scripts during cleaning gets fixed\n exit 0\nfi\n\necho $FPC_COMPILER_PATH $FPC_COMPILER_OPTIONS $FPC_MAIN_FILE\n\n$FPC_COMPILER_PATH $FPC_COMPILER_OPTIONS $FPC_MAIN_FILE";'#10+
+ ' };'#10+
+ '/* End PBXShellScriptBuildPhase section */'#10+
+ ''#10+
+ '/* Begin XCBuildConfiguration section */'#10+
+ ' 0A52AE8510F0D05300478C4F /* Release */ = {'#10+
+ ' isa = XCBuildConfiguration;'#10+
+ ' buildSettings = {'#10+
+ ' ARCHS = "$(ARCHS_STANDARD_32_BIT)";'#10+
+ ' COPY_PHASE_STRIP = YES;'#10+
+ ' FPC_OUTPUT_FILE = $BUILT_PRODUCTS_DIR/$EXECUTABLE_PATH;'#10+
+ ' FPC_COMPILER_OPTIONS = "-Parm -o$FPC_OUTPUT_FILE $FPC_CUSTOM_OPTIONS";'#10+
+ ' FPC_COMPILER_PATH = ;'#10+
+ ' FPC_CUSTOM_OPTIONS = ;'#10+
+ ' FPC_MAIN_FILE = ;'#10+
+ ' SDKROOT = iphoneos2.0;'#10+
+ ' VALID_ARCHS = "armv6 armv7";'#10+
+ ' };'#10+
+ ' name = Release;'#10+
+ ' };'#10+
+ ' 0A85A8B310F0D28800AB8400 /* Release */ = {'#10+
+ ' isa = XCBuildConfiguration;'#10+
+ ' buildSettings = {'#10+
+ ' ALWAYS_SEARCH_USER_PATHS = YES;'#10+
+ ' COPY_PHASE_STRIP = YES;'#10+
+ ' DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";'#10+
+ ' GCC_ENABLE_FIX_AND_CONTINUE = NO;'#10+
+ ' GCC_PRECOMPILE_PREFIX_HEADER = YES;'#10+
+ ' GCC_PREFIX_HEADER = "$(SYSTEM_LIBRARY_DIR)/Frameworks/UIKit.framework/Headers/UIKit.h";'#10+
+ ' INFOPLIST_FILE = ??plist;'#10+
+ ' INSTALL_PATH = "$(HOME)/Applications";'#10+
+ ' OTHER_LDFLAGS = ('#10+
+ ' "-framework",'#10+
+ ' Foundation,'#10+
+ ' "-framework",'#10+
+ ' UIKit,'#10+
+ ' );'#10+
+ ' PREBINDING = NO;'#10+
+ ' PRODUCT_NAME = ??productname;'#10+
+ ' SDKROOT = iphoneos2.0;'#10+
+ ' ZERO_LINK = NO;'#10+
+ ' };'#10+
+ ' name = Release;'#10+
+ ' };'#10+
+ '/* End XCBuildConfiguration section */'#10+
+ ' '#10+
+ '/* Begin XCConfigurationList section */'#10+
+ ' 0A52AE8610F0D05300478C4F /* Build configuration list for PBXProject "project1" */ = {'#10+
+ ' isa = XCConfigurationList;'#10+
+ ' buildConfigurations = ('#10+
+ ' 0A52AE8510F0D05300478C4F /* Release */,'#10+
+ ' );'#10+
+ ' defaultConfigurationIsVisible = 0;'#10+
+ ' defaultConfigurationName = Release;'#10+
+ ' };'#10+
+ ' 0A85A8B410F0D28800AB8400 /* Build configuration list for PBXNativeTarget */ = {'#10+
+ ' isa = XCConfigurationList;'#10+
+ ' buildConfigurations = ('#10+
+ ' 0A85A8B310F0D28800AB8400 /* Release */,'#10+
+ ' );'#10+
+ ' defaultConfigurationIsVisible = 0;'#10+
+ ' defaultConfigurationName = Release;'#10+
+ ' };'#10+
+ '/* End XCConfigurationList section */'#10+
+ ' };'#10+
+ ' rootObject = 0A52AE8310F0D05300478C4F /* Project object */;'#10+
+ '}'#10;
+
+implementation
+
+function GetValueName(const Source: String; idx: Integer): String;
+var
+ i : integer;
+const
+ //todo: expand symbols charset
+ Symbols: set of char = [#9, #32, #10,#13,
+ '=',':',';','-','+','*','/','\','!','@','#',
+ '$','%','^','&','(',')','~','`','''','"' ];
+begin
+ for i:=idx to length(Source) do
+ if Source[i] in Symbols then begin
+ Result:=Copy(Source, idx, i-idx);
+ Exit;
+ end;
+ Result:=Copy(Source, idx, length(Source)-idx+1);
+end;
+
+function ChangeValues(const Prefix, Source: String; Values: TStrings): String;
+var
+ i : integer;
+ nm : string;
+ v : string;
+begin
+ Result:=Source;
+ i:=Pos(Prefix, Result);
+ while i>0 do begin
+ nm:=GetValueName(Result, i+length(Prefix));
+ Delete(Result, i, length(Prefix)+length(nm));
+ v:=Values.Values[nm];
+
+ if Pos(Prefix, v) <= 0 then // don't allow circular prefix used, to avoid infinite loops
+ Insert(v, Result, i);
+ i:=Pos(Prefix, Result);
+ end;
+end;
+
+procedure PrepareTemplateFile(Src, TemplateValues, BuildSettings: TStrings);
+//todo: Better code to update XCode project file!
+var
+ i, j : Integer;
+ nm, s, v : String;
+ isSettings : Boolean;
+ buildhash : TFPStringHashTable;
+
+begin
+ if not Assigned(Src) then Exit;
+
+ if Assigned(TemplateValues) then
+ for i:=0 to Src.Count-1 do
+ Src[i]:=ChangeValues('??', Src[i], TemplateValues);
+
+ isSettings:=false;
+
+ if Assigned(BuildSettings) and (BuildSettings.Count>0) then begin
+ buildhash := TFPStringHashTable.Create;
+
+ for i :=0 to BuildSettings.Count-1 do
+ buildhash.Add(BuildSettings.Names[i], BuildSettings.ValueFromIndex[i]);
+
+ for i:=0 to Src.Count-1 do begin
+ if not isSettings then
+ isSettings:=Pos('buildSettings', Src[i])>0
+ else begin
+ if Trim(Src[i])='};' then
+ isSettings:=false
+ else begin
+ j:=1;
+ s:=Src[i];
+ while (j<=length(s)) and (s[j] in [#9, #32]) do
+ inc(j);
+
+ nm:=GetValueName(s, j);
+
+ if Assigned(buildhash.Find(nm)) then begin
+ v:=buildhash.Items[nm];
+ Src[i]:=Copy(Src[i], 1, j-1)+nm+ ' = ' + v + ';';
+ end;
+ end;
+ end; {of else}
+ end;
+ buildhash.Free;
+ end;
+end;
+
+
+end.
+