lazarus/ide/initialsetupdlgs.pas
2011-10-02 12:38:14 +00:00

1415 lines
40 KiB
ObjectPascal

{
/***************************************************************************
initialsetupdlgs.pas
--------------------
Contains the dialogs to help users setup basic settings.
***************************************************************************/
***************************************************************************
* *
* This source is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This code 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. See the GNU *
* General Public License for more details. *
* *
* A copy of the GNU General Public License is available on the World *
* Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
* obtain it by writing to the Free Software Foundation, *
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
***************************************************************************
Author: Mattias Gaertner
Abstract:
Procedures and dialogs to check environment. The IDE uses these procedures
at startup to check for example the lazarus directory and warns if it looks
suspicious and choose another.
}
unit InitialSetupDlgs;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, contnrs, LCLProc, Forms, Controls, Buttons, Dialogs,
FileUtil, Graphics, ComCtrls, ExtCtrls, StdCtrls,
Laz_XMLCfg,
DefineTemplates, CodeToolManager,
TextTools, IDEDialogs,
TransferMacros, LazarusIDEStrConsts, LazConf, EnvironmentOpts, IDEProcs,
AboutFrm;
type
TSDFilenameQuality = (
sddqInvalid,
sddqWrongVersion,
sddqIncomplete,
sddqCompatible
);
TSDFileInfo = class
public
Filename: string;
Caption: string;
Note: string;
Quality: TSDFilenameQuality;
end;
TSDFilenameType = (
sddtLazarusSrcDir,
sddtCompilerFilename,
sddtFPCSrcDir
);
{ TInitialSetupDialog }
TInitialSetupDialog = class(TForm)
BtnPanel: TPanel;
CompilerBrowseButton: TButton;
CompilerComboBox: TComboBox;
CompilerLabel: TLabel;
CompilerMemo: TMemo;
FPCSrcDirBrowseButton: TButton;
FPCSrcDirComboBox: TComboBox;
FPCSrcDirLabel: TLabel;
FPCSrcDirMemo: TMemo;
ImageList1: TImageList;
LazDirBrowseButton: TButton;
LazDirLabel: TLabel;
LazDirComboBox: TComboBox;
LazDirMemo: TMemo;
PropertiesPageControl: TPageControl;
PropertiesTreeView: TTreeView;
Splitter1: TSplitter;
StartIDEBitBtn: TBitBtn;
LazarusTabSheet: TTabSheet;
CompilerTabSheet: TTabSheet;
FPCSourcesTabSheet: TTabSheet;
WelcomePaintBox: TPaintBox;
procedure CompilerBrowseButtonClick(Sender: TObject);
procedure CompilerComboBoxChange(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FPCSrcDirBrowseButtonClick(Sender: TObject);
procedure FPCSrcDirComboBoxChange(Sender: TObject);
procedure LazDirBrowseButtonClick(Sender: TObject);
procedure LazDirComboBoxChange(Sender: TObject);
procedure PropertiesPageControlChange(Sender: TObject);
procedure PropertiesTreeViewSelectionChanged(Sender: TObject);
procedure StartIDEBitBtnClick(Sender: TObject);
procedure WelcomePaintBoxPaint(Sender: TObject);
procedure OnIdle(Sender: TObject; var Done: Boolean);
private
FFPCSrcNeedsUpdate: boolean;
FCompilerNeedsUpdate: boolean;
FFPCVer: string;
FIdleConnected: boolean;
FLazarusDir: string;
ImgIDError: LongInt;
FHeadGraphic: TPortableNetworkGraphic;
FSelectingPage: boolean;
FDirs: array[TSDFilenameType] of TObjectList; // list of TSDFileInfo
procedure SelectPage(const NodeText: string);
function SelectDirectory(aTitle: string): string;
procedure InitLazarusDir;
procedure InitCompilerFilename;
procedure InitFPCSrcDir;
procedure FillComboboxWithFileInfoList(ABox: TComboBox; List: TObjectList;
ItemIndex: integer = 0);
procedure SetFPCVer(const AValue: string);
procedure SetIdleConnected(const AValue: boolean);
procedure SetLazarusDir(const AValue: string);
procedure UpdateLazDirNote;
procedure UpdateCompilerNote;
procedure UpdateFPCSrcDirNote;
function FirstErrorNode: TTreeNode;
function GetCurrentLazarusDir: string;
function GetCurrentCompilerFilename: string;
function GetCurrentFPCSrcDir: string;
public
TVNodeLazarus: TTreeNode;
TVNodeCompiler: TTreeNode;
TVNodeFPCSources: TTreeNode;
procedure Init;
property LazarusDir: string read FLazarusDir write SetLazarusDir; // expanded
property FPCVer: string read FFPCVer write SetFPCVer;
property IdleConnected: boolean read FIdleConnected write SetIdleConnected;
end;
function ShowInitialSetupDialog: TModalResult;
function CheckLazarusDirectoryQuality(ADirectory: string;
out Note: string): TSDFilenameQuality;
function SearchLazarusDirectoryCandidates(StopIfFits: boolean): TObjectList;
procedure SetupLazarusDirectory;
function CheckCompilerQuality(AFilename: string;
out Note: string; TestSrcFilename: string): TSDFilenameQuality;
function SearchCompilerCandidates(StopIfFits: boolean;
const LazarusDir, TestSrcFilename: string): TObjectList;
procedure SetupCompilerFilename;
function CheckFPCSrcDirQuality(ADirectory: string;
out Note: string; FPCVer: string): TSDFilenameQuality;
function SearchFPCSrcDirCandidates(StopIfFits: boolean;
const LazarusDir, FPCVer: string): TObjectList;
function GetValueFromPrimaryConfig(OptionFilename, Path: string): string;
function GetValueFromSecondaryConfig(OptionFilename, Path: string): string;
function GetValueFromIDEConfig(OptionFilename, Path: string): string;
implementation
type
{ TSetupMacros }
TSetupMacros = class(TTransferMacroList)
protected
procedure DoSubstitution(TheMacro: TTransferMacro; const MacroName: string;
var s: string; const Data: PtrInt; var Handled, Abort: boolean;
Depth: integer); override;
public
FPCVer: string;
LazarusDir: string;
end;
function CheckLazarusDirectoryQuality(ADirectory: string;
out Note: string): TSDFilenameQuality;
function SubDirExists(SubDir: string; var q: TSDFilenameQuality): boolean;
begin
SubDir:=SetDirSeparators(SubDir);
if DirPathExistsCached(ADirectory+SubDir) then exit(true);
Result:=false;
Note:=Format(lisDirectoryNotFound2, [SubDir]);
q:=sddqIncomplete;
end;
function SubFileExists(SubFile: string; var q: TSDFilenameQuality): boolean;
begin
SubFile:=SetDirSeparators(SubFile);
if FileExistsCached(ADirectory+SubFile) then exit(true);
Result:=false;
Note:=Format(lisFileNotFound3, [SubFile]);
q:=sddqIncomplete;
end;
var
sl: TStringList;
VersionIncFile: String;
Version: String;
begin
Result:=sddqInvalid;
ADirectory:=TrimFilename(ADirectory);
if not DirPathExistsCached(ADirectory) then
begin
Note:=lisISDDirectoryNotFound;
exit;
end;
ADirectory:=AppendPathDelim(ADirectory);
if not SubDirExists('lcl',Result) then exit;
if not SubDirExists('packager/globallinks',Result) then exit;
if not SubDirExists('ide',Result) then exit;
if not SubDirExists('components',Result) then exit;
if not SubDirExists('ideintf',Result) then exit;
if not SubFileExists('ide/lazarus.lpi',Result) then exit;
VersionIncFile:=SetDirSeparators('ide/version.inc');
if not SubFileExists(VersionIncFile,Result) then exit;
sl:=TStringList.Create;
try
try
sl.LoadFromFile(ADirectory+VersionIncFile);
if (sl.Count=0) or (sl[0]='') or (sl[0][1]<>'''') then
begin
Note:=Format(lisInvalidVersionIn, [VersionIncFile]);
exit;
end;
Version:=copy(sl[0],2,length(sl[0])-2);
if Version<>LazarusVersionStr then
begin
Note:=Format(lisWrongVersionIn, [VersionIncFile, Version]);
Result:=sddqWrongVersion;
exit;
end;
Note:=rsOk;
Result:=sddqCompatible;
except
on E: Exception do begin
Note:=Format(lisUnableToLoadFile2, [VersionIncFile, E.Message]);
exit;
end;
end;
finally
sl.Free;
end;
end;
function SearchLazarusDirectoryCandidates(StopIfFits: boolean): TObjectList;
function CheckDir(Dir: string; var List: TObjectList): boolean;
var
Item: TSDFileInfo;
i: Integer;
begin
Result:=false;
Dir:=TrimFilename(Dir);
if Dir='' then exit;
Dir:=ChompPathDelim(ExpandFileNameUTF8(Dir));
// check if already checked
if List<>nil then begin
for i:=0 to List.Count-1 do
if CompareFilenames(Dir,TSDFileInfo(List[i]).Filename)=0 then exit;
end;
// check if exists
if not DirPathExistsCached(Dir) then exit;
// add to list and check quality
Item:=TSDFileInfo.Create;
Item.Filename:=Dir;
Item.Quality:=CheckLazarusDirectoryQuality(Dir,Item.Note);
Item.Caption:=Dir;
if List=nil then
List:=TObjectList.create(true);
List.Add(Item);
Result:=(Item.Quality=sddqCompatible) and StopIfFits;
end;
function CheckViaExe(Filename: string; var List: TObjectList): boolean;
begin
Result:=false;
Filename:=FindDefaultExecutablePath(Filename);
if Filename='' then exit;
Filename:=ReadAllLinks(Filename,false);
if Filename='' then exit;
Result:=CheckDir(ExtractFilePath(ExpandFileNameUTF8(Filename)),List);
end;
var
Dir: String;
ResolvedDir: String;
Dirs: TStringList;
i: Integer;
begin
Result:=nil;
// first check the value in the options
if CheckDir(EnvironmentOptions.LazarusDirectory,Result) then exit;
// then check the directory of the executable
Dir:=ProgramDirectory(true);
if CheckDir(Dir,Result) then exit;
ResolvedDir:=ReadAllLinks(Dir,false);
if (ResolvedDir<>Dir) and (CheckDir(ResolvedDir,Result)) then exit;
// check the primary options
Dir:=GetValueFromPrimaryConfig(EnvOptsConfFileName,
'EnvironmentOptions/LazarusDirectory/Value');
if CheckDir(Dir,Result) then exit;
// check the secondary options
Dir:=GetValueFromSecondaryConfig(EnvOptsConfFileName,
'EnvironmentOptions/LazarusDirectory/Value');
if CheckDir(Dir,Result) then exit;
// check common directories
Dirs:=GetDefaultLazarusSrcDirectories;
try
for i:=0 to Dirs.Count-1 do
if CheckDir(Dirs[i],Result) then exit;
finally
Dirs.Free;
end;
// check history
Dirs:=EnvironmentOptions.LazarusDirHistory;
if Dirs<>nil then
for i:=0 to Dirs.Count-1 do
if CheckDir(Dirs[i],Result) then exit;
// search lazarus-ide and lazarus in PATH, then follow the links,
// which will lead to the lazarus directory
if CheckViaExe('lazarus-ide'+GetExecutableExt,Result) then exit;
if CheckViaExe('lazarus'+GetExecutableExt,Result) then exit;
end;
procedure SetupLazarusDirectory;
var
Note: string;
Dir: String;
Quality: TSDFilenameQuality;
BestDir: TSDFileInfo;
List: TObjectList;
begin
Dir:=EnvironmentOptions.LazarusDirectory;
Quality:=CheckLazarusDirectoryQuality(Dir,Note);
if Quality<>sddqInvalid then exit;
debugln(['SetupLazarusDirectory: The Lazarus directory "',Dir,'" is invalid (Error: ',Note,'). Searching a proper one ...']);
List:=SearchLazarusDirectoryCandidates(true);
try
BestDir:=nil;
if List<>nil then
BestDir:=TSDFileInfo(List[List.Count-1]);
if (BestDir=nil) or (BestDir.Quality=sddqInvalid) then begin
debugln(['SetupCompilerFilename: no proper Lazarus directory found.']);
exit;
end;
EnvironmentOptions.LazarusDirectory:=BestDir.Filename;
debugln(['SetupLazarusDirectory: using ',EnvironmentOptions.LazarusDirectory]);
finally
List.Free;
end;
end;
function CheckCompilerQuality(AFilename: string; out Note: string;
TestSrcFilename: string): TSDFilenameQuality;
var
CfgCache: TFPCTargetConfigCache;
i: LongInt;
begin
Result:=sddqInvalid;
AFilename:=TrimFilename(AFilename);
if not FileExistsCached(AFilename) then
begin
Note:=lisFileNotFound4;
exit;
end;
if not FileIsExecutableCached(AFilename) then
begin
Note:=lisFileIsNotAnExecutable;
exit;
end;
if TestSrcFilename<>'' then
begin
CfgCache:=CodeToolBoss.FPCDefinesCache.ConfigCaches.Find(
AFilename,'','','',true);
if CfgCache.NeedsUpdate then
CfgCache.Update(TestSrcFilename);
i:=CfgCache.IndexOfUsedCfgFile;
if i<0 then
begin
Note:=lisFpcCfgIsMissing;
exit;
end;
if not CfgCache.HasPPUs then
begin
Note:=lisSystemPpuNotFoundCheckYourFpcCfg;
exit;
end;
if CompareFileExt(CfgCache.Units['classes'],'ppu',false)<>0 then
begin
Note:=lisClassesPpuNotFoundCheckYourFpcCfg;
exit;
end;
end;
Note:=rsOk;
Result:=sddqCompatible;
end;
function SearchCompilerCandidates(StopIfFits: boolean;
const LazarusDir, TestSrcFilename: string): TObjectList;
var
Macros: TSetupMacros;
Target: String;
ShortCompFile: String;
function CheckFile(AFilename: string; var List: TObjectList): boolean;
var
Item: TSDFileInfo;
i: Integer;
ResolvedFilename: String;
begin
Result:=false;
AFilename:=TrimFilename(AFilename);
if AFilename='' then exit;
// check if already checked
if List<>nil then
begin
for i:=0 to List.Count-1 do
if CompareFilenames(AFilename,TSDFileInfo(List[i]).Filename)=0 then exit;
end;
// replace macros
ResolvedFilename:=AFilename;
if TSetupMacros.StrHasMacros(ResolvedFilename) then
begin
if Macros=nil then
begin
Macros:=TSetupMacros.Create;
Macros.LazarusDir:=LazarusDir;
end;
if not Macros.SubstituteStr(ResolvedFilename) then exit;
ResolvedFilename:=TrimFilename(ResolvedFilename);
if ResolvedFilename='' then exit;
end;
// expand file name
ResolvedFilename:=ExpandFileNameUTF8(ResolvedFilename);
// check if exists
if not FileExistsCached(ResolvedFilename) then exit;
// add to list and check quality
Item:=TSDFileInfo.Create;
Item.Filename:=AFilename;
Item.Quality:=CheckCompilerQuality(ResolvedFilename,Item.Note,TestSrcFilename);
Item.Caption:=AFilename;
if List=nil then
List:=TObjectList.create(true);
List.Add(Item);
Result:=(Item.Quality=sddqCompatible) and StopIfFits;
end;
function CheckSubDirs(ADir: string; var List: TObjectList): boolean;
// search for ADir\bin\i386-win32\fpc.exe
// and for ADir\*\bin\i386-win32\fpc.exe
var
FileInfo: TSearchRec;
SubFile: String;
begin
Result:=true;
ADir:=AppendPathDelim(TrimFilename(ExpandFileNameUTF8(TrimFilename(ADir))));
SubFile:='bin'+PathDelim+Target+PathDelim+ShortCompFile;
if CheckFile(ADir+SubFile,List) then
exit;
try
if FindFirstUTF8(ADir+GetAllFilesMask,faAnyFile,FileInfo)=0 then begin
repeat
// check if special file
if (FileInfo.Name='.') or (FileInfo.Name='..') or (FileInfo.Name='') then
continue;
if ((FileInfo.Attr and faDirectory)>0)
and CheckFile(ADir+FileInfo.Name+PathDelim+SubFile,List) then
exit;
until FindNextUTF8(FileInfo)<>0;
end;
finally
FindCloseUTF8(FileInfo);
end;
Result:=false;
end;
var
AFilename: String;
Files: TStringList;
i: Integer;
SysDrive: String;
ProgDir: String;
begin
Result:=nil;
Macros:=nil;
try
// check current setting
if CheckFile(EnvironmentOptions.CompilerFilename,Result) then exit;
// check the primary options
AFilename:=GetValueFromPrimaryConfig(EnvOptsConfFileName,
'EnvironmentOptions/CompilerFilename/Value');
if CheckFile(AFilename,Result) then exit;
// check the secondary options
AFilename:=GetValueFromSecondaryConfig(EnvOptsConfFileName,
'EnvironmentOptions/CompilerFilename/Value');
if CheckFile(AFilename,Result) then exit;
// check PATH
if CheckFile(FindDefaultCompilerPath,Result) then exit;
// check history
Files:=EnvironmentOptions.CompilerFileHistory;
if Files<>nil then
for i:=0 to Files.Count-1 do
if CheckFile(Files[i],Result) then exit;
// check common directories
Files:=TStringList.Create;
try
GetDefaultCompilerFilenames(Files);
for i:=0 to Files.Count-1 do
if CheckFile(Files[i],Result) then exit;
finally
Files.Free;
end;
// check paths with versions
Target:=GetCompiledTargetCPU+'-'+GetCompiledTargetOS;
ShortCompFile:=GetDefaultCompilerFilename;
// check $(LazDir)\fpc\bin\i386-win32\fpc.exe
if (LazarusDir<>'')
and CheckFile(AppendPathDelim(LazarusDir)
+SetDirSeparators('fpc/bin/'+Target+'/'+ShortCompFile),Result)
then exit;
if (GetDefaultSrcOSForTargetOS(GetCompiledTargetOS)='win') then begin
// windows has some special places
SysDrive:=GetEnvironmentVariableUTF8('SYSTEMDRIVE');
if SysDrive='' then SysDrive:='C:';
SysDrive:=AppendPathDelim(SysDrive);
// %SYSTEMDRIVE%\fpc\
if CheckSubDirs(SysDrive+'FPC',Result) then exit;
// %SYSTEMDRIVE%\pp\
if CheckSubDirs(SysDrive+'pp',Result) then exit;
// %PROGRAMFILES%\FPC\*
ProgDir:=AppendPathDelim(GetEnvironmentVariableUTF8('PROGRAMFILES'));
if (ProgDir<>'')
and CheckSubDirs(ProgDir+'FPC',Result) then exit;
end;
finally
if Macros<>nil then
Macros.Free;
end;
end;
procedure SetupCompilerFilename;
var
Note: string;
CompFile: String;
Quality: TSDFilenameQuality;
BestDir: TSDFileInfo;
List: TObjectList;
begin
CompFile:=EnvironmentOptions.GetCompilerFilename;
Quality:=CheckCompilerQuality(CompFile,Note,'');
if Quality<>sddqInvalid then exit;
debugln(['SetupCompilerFilename: The compiler path "',CompFile,'" is invalid (Error: ',Note,'). Searching a proper one ...']);
List:=SearchCompilerCandidates(true,EnvironmentOptions.LazarusDirectory,
CodeToolBoss.FPCDefinesCache.TestFilename);
try
BestDir:=nil;
if List<>nil then
BestDir:=TSDFileInfo(List[List.Count-1]);
if (BestDir=nil) or (BestDir.Quality=sddqInvalid) then begin
debugln(['SetupCompilerFilename: no proper compiler found.']);
exit;
end;
EnvironmentOptions.CompilerFilename:=BestDir.Filename;
debugln(['SetupCompilerFilename: using ',EnvironmentOptions.CompilerFilename]);
finally
List.Free;
end;
end;
function CheckFPCSrcDirQuality(ADirectory: string; out Note: string;
FPCVer: string): TSDFilenameQuality;
function SubDirExists(SubDir: string; var q: TSDFilenameQuality): boolean;
begin
SubDir:=SetDirSeparators(SubDir);
if DirPathExistsCached(ADirectory+SubDir) then exit(true);
Result:=false;
Note:=Format(lisDirectoryNotFound2, [SubDir]);
q:=sddqIncomplete;
end;
function SubFileExists(SubFile: string; var q: TSDFilenameQuality): boolean;
begin
SubFile:=SetDirSeparators(SubFile);
if FileExistsCached(ADirectory+SubFile) then exit(true);
Result:=false;
Note:=Format(lisFileNotFound3, [SubFile]);
q:=sddqIncomplete;
end;
var
VersionFile: String;
sl: TStringList;
i: Integer;
VersionNr: String;
ReleaseNr: String;
PatchNr: String;
SrcVer: String;
begin
Result:=sddqInvalid;
Note:='';
ADirectory:=TrimFilename(ADirectory);
if not DirPathExistsCached(ADirectory) then
begin
Note:=lisISDDirectoryNotFound;
exit;
end;
ADirectory:=AppendPathDelim(ADirectory);
if not SubDirExists('rtl',Result) then exit;
if not SubDirExists('packages',Result) then exit;
if not SubFileExists('rtl/linux/system.pp',Result) then exit;
// check version
if (FPCVer<>'') then
begin
VersionFile:=ADirectory+'compiler'+PathDelim+'version.pas';
if FileExistsCached(VersionFile) then
begin
sl:=TStringList.Create;
try
try
sl.LoadFromFile(VersionFile);
for i:=0 to sl.Count-1 do
begin
if REMatches(sl[i],' version_nr *= *''([0-9]+)''','I') then
VersionNr:=REVar(1)
else if REMatches(sl[i],' release_nr *= *''([0-9]+)''','I') then
ReleaseNr:=REVar(1)
else if REMatches(sl[i],' patch_nr *= *''([0-9]+)''','I') then begin
PatchNr:=REVar(1);
break;
end;
end;
SrcVer:=VersionNr+'.'+ReleaseNr+'.'+PatchNr;
if SrcVer<>FPCVer then
begin
Note:=Format(lisFoundVersionExpected, [SrcVer, FPCVer]);
Result:=sddqWrongVersion;
exit;
end;
except
end;
finally
sl.Free;
end;
end;
end;
Note:=rsOk;
Result:=sddqCompatible;
end;
function SearchFPCSrcDirCandidates(StopIfFits: boolean;
const LazarusDir, FPCVer: string): TObjectList;
var
Macros: TSetupMacros;
function InList(AFilename: string; List: TObjectList): boolean;
var
i: Integer;
begin
Result:=false;
if List=nil then exit;
for i:=0 to List.Count-1 do
if CompareFilenames(AFilename,TSDFileInfo(List[i]).Filename)=0 then
exit(true);
end;
function Check(AFilename: string; var List: TObjectList): boolean;
var
Item: TSDFileInfo;
ResolvedFilename: String;
begin
Result:=false;
AFilename:=TrimFilename(AFilename);
if AFilename='' then exit;
// check if already checked
if InList(AFilename,List) then exit;
ResolvedFilename:=AFilename;
// replace macros
if TSetupMacros.StrHasMacros(ResolvedFilename) then
begin
if Macros=nil then
begin
Macros:=TSetupMacros.Create;
Macros.LazarusDir:=LazarusDir;
Macros.FPCVer:=FPCVer;
end;
if not Macros.SubstituteStr(ResolvedFilename) then exit;
ResolvedFilename:=TrimFilename(ResolvedFilename);
if ResolvedFilename='' then exit;
end;
// expand file name
ResolvedFilename:=ChompPathDelim(ExpandFileNameUTF8(ResolvedFilename));
// check if exists
if not DirPathExistsCached(ResolvedFilename) then exit;
// add to list and check quality
Item:=TSDFileInfo.Create;
Item.Filename:=AFilename;
Item.Quality:=CheckFPCSrcDirQuality(ResolvedFilename,Item.Note,FPCVer);
Item.Caption:=AFilename;
if List=nil then
List:=TObjectList.create(true);
List.Add(Item);
Result:=(Item.Quality=sddqCompatible) and StopIfFits;
end;
var
AFilename: String;
Dirs: TStringList;
i: Integer;
begin
Result:=nil;
Macros:=nil;
try
// check current setting
if Check(EnvironmentOptions.FPCSourceDirectory,Result) then exit;
// check the primary options
AFilename:=GetValueFromPrimaryConfig(EnvOptsConfFileName,
'EnvironmentOptions/FPCSourceDirectory/Value');
if Check(AFilename,Result) then exit;
// check the secondary options
AFilename:=GetValueFromSecondaryConfig(EnvOptsConfFileName,
'EnvironmentOptions/FPCSourceDirectory/Value');
if Check(AFilename,Result) then exit;
// check history
Dirs:=EnvironmentOptions.FPCSourceDirHistory;
if Dirs<>nil then
for i:=0 to Dirs.Count-1 do
if Check(Dirs[i],Result) then exit;
// check common directories
Dirs:=GetDefaultFPCSrcDirectories;
try
if Dirs<>nil then
for i:=0 to Dirs.Count-1 do
if Check(Dirs[i],Result) then exit;
finally
Dirs.Free;
end;
finally
if Macros<>nil then
Macros.Free;
end;
end;
function GetValueFromPrimaryConfig(OptionFilename, Path: string): string;
begin
if not FilenameIsAbsolute(OptionFilename) then
OptionFilename:=AppendPathDelim(GetPrimaryConfigPath)+OptionFilename;
Result:=GetValueFromIDEConfig(OptionFilename,Path);
end;
function GetValueFromSecondaryConfig(OptionFilename, Path: string): string;
begin
if not FilenameIsAbsolute(OptionFilename) then
OptionFilename:=AppendPathDelim(GetSecondaryConfigPath)+OptionFilename;
Result:=GetValueFromIDEConfig(OptionFilename,Path);
end;
function GetValueFromIDEConfig(OptionFilename, Path: string): string;
var
XMLConfig: TXMLConfig;
begin
if FileExistsCached(OptionFilename) then
begin
try
XMLConfig:=TXMLConfig.Create(OptionFilename);
try
Result:=XMLConfig.GetValue(Path,'');
finally
XMLConfig.Free;
end;
except
on E: Exception do begin
debugln(['GetValueFromIDEConfig File='+OptionFilename+': '+E.Message]);
end;
end;
end;
end;
function ShowInitialSetupDialog: TModalResult;
var
InitialSetupDialog: TInitialSetupDialog;
begin
InitialSetupDialog:=TInitialSetupDialog.Create(nil);
try
InitialSetupDialog.Init;
Result:=InitialSetupDialog.ShowModal;
finally
InitialSetupDialog.Free;
end;
end;
{ TSetupMacros }
procedure TSetupMacros.DoSubstitution(TheMacro: TTransferMacro;
const MacroName: string; var s: string; const Data: PtrInt; var Handled,
Abort: boolean; Depth: integer);
begin
Handled:=true;
if CompareText(MacroName,'ENV')=0 then
s:=GetEnvironmentVariableUTF8(MacroName)
else if CompareText(MacroName,'PrimaryConfigPath')=0 then
s:=GetPrimaryConfigPath
else if CompareText(MacroName,'SecondaryConfigPath')=0 then
s:=GetSecondaryConfigPath
else if CompareText(MacroName,'FPCVer')=0 then begin
if FPCVer<>'' then
s:=FPCVer
else
s:={$I %FPCVERSION%};
end else if CompareText(MacroName,'LazarusDir')=0 then begin
if LazarusDir<>'' then
s:=LazarusDir
else
s:='<LazarusDirNotSet>';
end else if CompareText(MacroName,'TargetOS')=0 then
s:=GetCompiledTargetOS
else if CompareText(MacroName,'TargetCPU')=0 then
s:=GetCompiledTargetCPU
else if CompareText(MacroName,'SrcOS')=0 then
s:=GetDefaultSrcOSForTargetOS(GetCompiledTargetOS)
else
Handled:=false;
//debugln(['TSetupMacros.DoSubstitution MacroName=',MacroName,' Value="',s,'"']);
end;
{$R *.lfm}
{ TInitialSetupDialog }
procedure TInitialSetupDialog.FormCreate(Sender: TObject);
begin
Caption:=Format(lisWelcomeToLazarusIDE, [GetLazarusVersionString]);
StartIDEBitBtn.Caption:=lisStartIDE;
LazarusTabSheet.Caption:='Lazarus';
CompilerTabSheet.Caption:=lisCompiler;
FPCSourcesTabSheet.Caption:=lisFPCSources;
FHeadGraphic:=TPortableNetworkGraphic.Create;
FHeadGraphic.LoadFromLazarusResource('ide_icon48x48');
TVNodeLazarus:=PropertiesTreeView.Items.Add(nil,LazarusTabSheet.Caption);
TVNodeCompiler:=PropertiesTreeView.Items.Add(nil,CompilerTabSheet.Caption);
TVNodeFPCSources:=PropertiesTreeView.Items.Add(nil,FPCSourcesTabSheet.Caption);
ImgIDError := ImageList1.AddLazarusResource('state_error');
LazDirBrowseButton.Caption:=lisPathEditBrowse;
LazDirLabel.Caption:=Format(
lisTheLazarusDirectoryContainsTheSourcesOfTheIDEAndTh, [PathDelim]);
CompilerBrowseButton.Caption:=lisPathEditBrowse;
CompilerLabel.Caption:=Format(
lisTheFreePascalCompilerExecutableTypicallyHasTheName, [DefineTemplates.
GetDefaultCompilerFilename, DefineTemplates.GetDefaultCompilerFilename(
GetCompiledTargetCPU)]);
FPCSrcDirBrowseButton.Caption:=lisPathEditBrowse;
FPCSrcDirLabel.Caption:=Format(
lisTheSourcesOfTheFreePascalPackagesAreRequiredForBro, [SetDirSeparators('rtl'
+'/linux/system.pp')]);
end;
procedure TInitialSetupDialog.CompilerComboBoxChange(Sender: TObject);
begin
UpdateCompilerNote;
end;
procedure TInitialSetupDialog.CompilerBrowseButtonClick(Sender: TObject);
var
Filename: String;
Dlg: TOpenDialog;
Filter: String;
begin
Dlg:=TOpenDialog.Create(nil);
try
Filename:='fpc'+GetExecutableExt;
Dlg.Title:=Format(lisSelectPathTo, [Filename]);
Dlg.Options:=Dlg.Options+[ofFileMustExist];
Filter:=dlgAllFiles+'|'+GetAllFilesMask;
if ExtractFileExt(Filename)<>'' then
Filter:=lisExecutable+'|*'+ExtractFileExt(Filename)+'|'+Filter;
Dlg.Filter:=Filter;
if not Dlg.Execute then exit;
Filename:=Dlg.FileName;
finally
Dlg.Free;
end;
CompilerComboBox.Text:=Filename;
UpdateCompilerNote;
end;
procedure TInitialSetupDialog.FormDestroy(Sender: TObject);
var
d: TSDFilenameType;
begin
IdleConnected:=false;
for d:=low(FDirs) to high(FDirs) do
FreeAndNil(FDirs[d]);
FreeAndNil(FHeadGraphic);
end;
procedure TInitialSetupDialog.FPCSrcDirBrowseButtonClick(Sender: TObject);
var
Dir: String;
begin
Dir:=SelectDirectory(lisSelectFPCSourceDirectory);
if Dir='' then exit;
FPCSrcDirComboBox.Text:=Dir;
UpdateFPCSrcDirNote;
end;
procedure TInitialSetupDialog.FPCSrcDirComboBoxChange(Sender: TObject);
begin
UpdateFPCSrcDirNote;
end;
procedure TInitialSetupDialog.LazDirBrowseButtonClick(Sender: TObject);
var
Dir: String;
begin
Dir:=SelectDirectory(lisSelectLazarusSourceDirectory);
if Dir='' then exit;
LazDirComboBox.Text:=Dir;
UpdateLazDirNote;
end;
procedure TInitialSetupDialog.LazDirComboBoxChange(Sender: TObject);
begin
UpdateLazDirNote;
end;
procedure TInitialSetupDialog.PropertiesPageControlChange(Sender: TObject);
var
s: String;
i: Integer;
begin
if PropertiesPageControl.ActivePage=nil then exit;
s:=PropertiesPageControl.ActivePage.Caption;
for i:=0 to PropertiesTreeView.Items.TopLvlCount-1 do
if PropertiesTreeView.Items.TopLvlItems[i].Text=s then
PropertiesTreeView.Selected:=PropertiesTreeView.Items.TopLvlItems[i];
end;
procedure TInitialSetupDialog.PropertiesTreeViewSelectionChanged(Sender: TObject
);
begin
if PropertiesTreeView.Selected=nil then
SelectPage(TVNodeLazarus.Text)
else
SelectPage(PropertiesTreeView.Selected.Text);
end;
procedure TInitialSetupDialog.StartIDEBitBtnClick(Sender: TObject);
var
Node: TTreeNode;
s: String;
MsgResult: TModalResult;
begin
Node:=FirstErrorNode;
if Node=TVNodeLazarus then
s:=lisWithoutAProperLazarusDirectoryYouWillGetALotOfWarn
else if Node=TVNodeCompiler then
s:=lisWithoutAProperCompilerTheCodeBrowsingAndCompilingW
else if Node=TVNodeFPCSources then
s:=lisWithoutTheProperFPCSourcesCodeBrowsingAndCompletio;
if s<>'' then begin
MsgResult:=MessageDlg(lisCCOWarningCaption, s, mtWarning, [mbIgnore,
mbCancel], 0);
if MsgResult<>mrIgnore then exit;
end;
s:=GetCurrentLazarusDir;
if s<>'' then EnvironmentOptions.LazarusDirectory:=s;
s:=GetCurrentCompilerFilename;
if s<>'' then
EnvironmentOptions.CompilerFilename:=s;
s:=GetCurrentFPCSrcDir;
if s<>'' then
EnvironmentOptions.FPCSourceDirectory:=s;
ModalResult:=mrOk;
end;
procedure TInitialSetupDialog.WelcomePaintBoxPaint(Sender: TObject);
begin
with WelcomePaintBox.Canvas do begin
GradientFill(WelcomePaintBox.ClientRect,$854b32,$c88e60,gdHorizontal);
Draw(0,WelcomePaintBox.ClientHeight-FHeadGraphic.Height,FHeadGraphic);
Font.Color:=clWhite;
Font.Height:=30;
Brush.Style:=bsClear;
TextOut(FHeadGraphic.Width+15, 5, lisConfigureLazarusIDE);
end;
end;
procedure TInitialSetupDialog.OnIdle(Sender: TObject; var Done: Boolean);
begin
if FCompilerNeedsUpdate then
InitCompilerFilename
else if FFPCSrcNeedsUpdate then
InitFPCSrcDir
else
IdleConnected:=false;
end;
procedure TInitialSetupDialog.SelectPage(const NodeText: string);
var
i: Integer;
Node: TTreeNode;
begin
if FSelectingPage then exit;
FSelectingPage:=true;
try
for i:=0 to PropertiesTreeView.Items.TopLvlCount-1 do begin
Node:=PropertiesTreeView.Items.TopLvlItems[i];
if Node.Text=NodeText then begin
PropertiesTreeView.Selected:=Node;
PropertiesPageControl.ActivePageIndex:=i;
break;
end;
end;
finally
FSelectingPage:=false;
end;
end;
function TInitialSetupDialog.SelectDirectory(aTitle: string): string;
var
DirDlg: TSelectDirectoryDialog;
begin
Result:='';
DirDlg:=TSelectDirectoryDialog.Create(nil);
try
DirDlg.Title:=aTitle;
DirDlg.Options:=DirDlg.Options+[ofPathMustExist,ofFileMustExist];
if not DirDlg.Execute then exit;
Result:=DirDlg.FileName;
finally
DirDlg.Free;
end;
end;
procedure TInitialSetupDialog.InitLazarusDir;
var
Dirs: TObjectList;
begin
FreeAndNil(FDirs[sddtLazarusSrcDir]);
Dirs:=SearchLazarusDirectoryCandidates(false);;
FDirs[sddtLazarusSrcDir]:=Dirs;
FillComboboxWithFileInfoList(LazDirComboBox,Dirs);
UpdateLazDirNote;
end;
procedure TInitialSetupDialog.InitCompilerFilename;
var
Files: TObjectList;
begin
FCompilerNeedsUpdate:=false;
FreeAndNil(FDirs[sddtCompilerFilename]);
Files:=SearchCompilerCandidates(false,LazDirComboBox.Text,
CodeToolBoss.FPCDefinesCache.TestFilename);
FDirs[sddtCompilerFilename]:=Files;
FillComboboxWithFileInfoList(CompilerComboBox,Files);
UpdateCompilerNote;
end;
procedure TInitialSetupDialog.InitFPCSrcDir;
var
Dirs: TObjectList;
begin
FFPCSrcNeedsUpdate:=false;
FreeAndNil(FDirs[sddtFPCSrcDir]);
Dirs:=SearchFPCSrcDirCandidates(false,LazDirComboBox.Text,FPCVer);
FDirs[sddtFPCSrcDir]:=Dirs;
FillComboboxWithFileInfoList(FPCSrcDirComboBox,Dirs);
UpdateFPCSrcDirNote;
end;
procedure TInitialSetupDialog.FillComboboxWithFileInfoList(ABox: TComboBox;
List: TObjectList; ItemIndex: integer);
var
sl: TStringList;
i: Integer;
begin
sl:=TStringList.Create;
try
if List<>nil then
for i:=0 to List.Count-1 do
sl.Add(TSDFileInfo(List[i]).Caption);
ABox.Items.Assign(sl);
if (ItemIndex>=0) and (ItemIndex<sl.Count) then
ABox.Text:=sl[ItemIndex]
else if ABox.Text=ABox.Name then
ABox.Text:='';
finally
sl.Free;
end;
end;
procedure TInitialSetupDialog.SetFPCVer(const AValue: string);
begin
if FFPCVer=AValue then exit;
FFPCVer:=AValue;
FFPCSrcNeedsUpdate:=true;
end;
procedure TInitialSetupDialog.SetIdleConnected(const AValue: boolean);
begin
if FIdleConnected=AValue then exit;
FIdleConnected:=AValue;
if IdleConnected then
Application.AddOnIdleHandler(@OnIdle)
else
Application.RemoveOnIdleHandler(@OnIdle);
end;
procedure TInitialSetupDialog.SetLazarusDir(const AValue: string);
begin
if FLazarusDir=AValue then exit;
FLazarusDir:=AValue;
FCompilerNeedsUpdate:=true;
end;
procedure TInitialSetupDialog.UpdateLazDirNote;
function NormDir(const Dir: string): string;
begin
Result:=ChompPathDelim(TrimFilename(Dir));
if Result<>'' then
Result:=ChompPathDelim(TrimFilename(ExpandFileNameUTF8(Result)));
end;
var
i: Integer;
Dirs: TObjectList;
CurCaption: String;
Note: string;
Quality: TSDFilenameQuality;
s: String;
ImageIndex: Integer;
begin
i:=-1;
Dirs:=FDirs[sddtLazarusSrcDir];
CurCaption:=LazDirComboBox.Text;
if Dirs<>nil then begin
i:=Dirs.Count-1;
while (i>=0) and (TSDFileInfo(Dirs[i]).Caption<>CurCaption) do dec(i);
end;
if i>=0 then begin
Quality:=TSDFileInfo(Dirs[i]).Quality;
Note:=TSDFileInfo(Dirs[i]).Note;
LazarusDir:=NormDir(TSDFileInfo(Dirs[i]).Filename);
end else begin
LazarusDir:=NormDir(CurCaption);
Quality:=CheckLazarusDirectoryQuality(CurCaption,Note);
end;
case Quality of
sddqInvalid: s:=lisError;
sddqCompatible: s:='';
else s:=lisWarning;
end;
LazDirMemo.Text:=s+Note;
if Quality=sddqCompatible then
ImageIndex:=-1
else
ImageIndex:=ImgIDError;
TVNodeLazarus.ImageIndex:=ImageIndex;
TVNodeLazarus.SelectedIndex:=ImageIndex;
end;
procedure TInitialSetupDialog.UpdateCompilerNote;
function NormFile(const AFilename: string): string;
var
Macros: TSetupMacros;
begin
Result:=TrimFilename(AFilename);
if TSetupMacros.StrHasMacros(Result) then begin
Macros:=TSetupMacros.Create;
try
Macros.LazarusDir:=LazarusDir;
Macros.SubstituteStr(Result);
finally
Macros.Free;
end;
end;
if Result<>'' then
Result:=TrimFilename(ExpandFileNameUTF8(Result));
end;
var
i: Integer;
Files: TObjectList;
CurCaption: String;
Note: string;
Quality: TSDFilenameQuality;
s: String;
ImageIndex: Integer;
CompilerFile: String;
CfgCache: TFPCTargetConfigCache;
begin
i:=-1;
Files:=FDirs[sddtCompilerFilename];
CurCaption:=CompilerComboBox.Text;
if Files<>nil then begin
i:=Files.Count-1;
while (i>=0) and (TSDFileInfo(Files[i]).Caption<>CurCaption) do dec(i);
end;
if i>=0 then begin
Quality:=TSDFileInfo(Files[i]).Quality;
Note:=TSDFileInfo(Files[i]).Note;
CompilerFile:=NormFile(TSDFileInfo(Files[i]).Filename);
end else begin
CompilerFile:=NormFile(CurCaption);
Quality:=CheckCompilerQuality(CurCaption,Note,
CodeToolBoss.FPCDefinesCache.TestFilename);
end;
if Quality=sddqInvalid then
FPCVer:=''
else begin
CfgCache:=CodeToolBoss.FPCDefinesCache.ConfigCaches.Find(
CompilerFile,'','','',true);
FPCVer:=CfgCache.GetFPCVer;
end;
case Quality of
sddqInvalid: s:=lisError;
sddqCompatible: s:='';
else s:=lisWarning;
end;
CompilerMemo.Text:=s+Note;
if Quality=sddqCompatible then
ImageIndex:=-1
else
ImageIndex:=ImgIDError;
TVNodeCompiler.ImageIndex:=ImageIndex;
TVNodeCompiler.SelectedIndex:=ImageIndex;
end;
procedure TInitialSetupDialog.UpdateFPCSrcDirNote;
function NormDir(const AFilename: string): string;
var
Macros: TSetupMacros;
begin
Result:=TrimFilename(AFilename);
if TSetupMacros.StrHasMacros(Result) then begin
Macros:=TSetupMacros.Create;
try
Macros.LazarusDir:=LazarusDir;
Macros.FPCVer:=FPCVer;
Macros.SubstituteStr(Result);
finally
Macros.Free;
end;
end;
Result:=ChompPathDelim(Result);
if Result<>'' then
Result:=ChompPathDelim(TrimFilename(ExpandFileNameUTF8(Result)));
end;
var
i: Integer;
Dirs: TObjectList;
CurCaption: String;
Note: string;
Quality: TSDFilenameQuality;
s: String;
ImageIndex: Integer;
begin
i:=-1;
Dirs:=FDirs[sddtFPCSrcDir];
CurCaption:=FPCSrcDirComboBox.Text;
if Dirs<>nil then begin
i:=Dirs.Count-1;
while (i>=0) and (TSDFileInfo(Dirs[i]).Caption<>CurCaption) do dec(i);
end;
if i>=0 then begin
Quality:=TSDFileInfo(Dirs[i]).Quality;
Note:=TSDFileInfo(Dirs[i]).Note;
end else begin
Quality:=CheckFPCSrcDirQuality(CurCaption,Note,FPCVer);
end;
case Quality of
sddqInvalid: s:=lisError;
sddqCompatible: s:='';
else s:=lisWarning;
end;
FPCSrcDirMemo.Text:=s+Note;
if Quality=sddqCompatible then
ImageIndex:=-1
else
ImageIndex:=ImgIDError;
TVNodeFPCSources.ImageIndex:=ImageIndex;
TVNodeFPCSources.SelectedIndex:=ImageIndex;
end;
function TInitialSetupDialog.FirstErrorNode: TTreeNode;
var
i: Integer;
begin
for i:=0 to PropertiesTreeView.Items.TopLvlCount-1 do
begin
Result:=PropertiesTreeView.Items.TopLvlItems[i];
if Result.ImageIndex=ImgIDError then exit;
end;
Result:=nil;
end;
function TInitialSetupDialog.GetCurrentLazarusDir: string;
var
Dirs: TObjectList;
i: Integer;
begin
Dirs:=FDirs[sddtLazarusSrcDir];
Result:=LazDirComboBox.Text;
if Dirs<>nil then begin
i:=Dirs.Count-1;
while (i>=0) and (TSDFileInfo(Dirs[i]).Caption<>Result) do dec(i);
if i>=0 then
Result:=TSDFileInfo(Dirs[i]).Filename;
end;
Result:=ChompPathDelim(TrimFilename(Result));
if Result<>'' then
Result:=ChompPathDelim(TrimFilename(ExpandFileNameUTF8(Result)));
end;
function TInitialSetupDialog.GetCurrentCompilerFilename: string;
var
Dirs: TObjectList;
i: Integer;
begin
Dirs:=FDirs[sddtCompilerFilename];
Result:=CompilerComboBox.Text;
if Dirs<>nil then begin
i:=Dirs.Count-1;
while (i>=0) and (TSDFileInfo(Dirs[i]).Caption<>Result) do dec(i);
if i>=0 then
Result:=TSDFileInfo(Dirs[i]).Filename;
end;
Result:=TrimFilename(Result);
end;
function TInitialSetupDialog.GetCurrentFPCSrcDir: string;
var
Dirs: TObjectList;
i: Integer;
begin
Dirs:=FDirs[sddtFPCSrcDir];
Result:=FPCSrcDirComboBox.Text;
if Dirs<>nil then begin
i:=Dirs.Count-1;
while (i>=0) and (TSDFileInfo(Dirs[i]).Caption<>Result) do dec(i);
if i>=0 then
Result:=TSDFileInfo(Dirs[i]).Filename;
end;
Result:=ChompPathDelim(TrimFilename(Result));
end;
procedure TInitialSetupDialog.Init;
var
Node: TTreeNode;
begin
InitLazarusDir;
InitCompilerFilename;
InitFPCSrcDir;
// select first error
Node:=FirstErrorNode;
if Node=nil then
Node:=TVNodeLazarus;
PropertiesTreeView.Selected:=Node;
end;
end.