1 {
2  /***************************************************************************
3                             initialsetupdlgs.pas
4                             --------------------
5        Contains the dialogs to help users setup basic settings.
6 
7 
8  ***************************************************************************/
9 
10  ***************************************************************************
11  *                                                                         *
12  *   This source is free software; you can redistribute it and/or modify   *
13  *   it under the terms of the GNU General Public License as published by  *
14  *   the Free Software Foundation; either version 2 of the License, or     *
15  *   (at your option) any later version.                                   *
16  *                                                                         *
17  *   This code is distributed in the hope that it will be useful, but      *
18  *   WITHOUT ANY WARRANTY; without even the implied warranty of            *
19  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
20  *   General Public License for more details.                              *
21  *                                                                         *
22  *   A copy of the GNU General Public License is available on the World    *
23  *   Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also      *
24  *   obtain it by writing to the Free Software Foundation,                 *
25  *   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.   *
26  *                                                                         *
27  ***************************************************************************
28 
29   Author: Mattias Gaertner
30 
31   Abstract:
32     Contains the dialogs to help users setup basic settings.
33 }
34 unit InitialSetupDlgs;
35 
36 {$mode objfpc}{$H+}
37 
38 {off $DEFINE VerboseFPCSrcScanThead}
39 
40 interface
41 
42 uses
43   // RTL + FCL
44   Classes, SysUtils, pkgglobals, fpmkunit,
45   // LCL
46   Forms, Controls, Buttons, Dialogs, Graphics, ComCtrls, ExtCtrls, StdCtrls,
47   // CodeTools
48   FileProcs, CodeToolManager, DefineTemplates,
49   // LazUtils
50   FileUtil, LazUTF8, LazFileUtils, LazStringUtils, LazFileCache, LazLoggerBase,
51   // IdeIntf
52   MacroDefIntf, IDEDialogs, IDEImagesIntf, IDEUtils,
53   // DebuggerIntf
54   DbgIntfDebuggerBase,
55   // LazDebuggerGdbmi
56   GDBMIDebugger,
57   // IDE
58   TransferMacros, LazarusIDEStrConsts, LazConf, EnvironmentOpts,
59   AboutFrm, IDETranslations, BaseBuildManager, InitialSetupProc,
60   {$IF FPC_FULLVERSION>30100}
61   GenerateFppkgConfigurationDlg,
62   {$ENDIF}
63   IDEProcs;
64 
65 type
66   TInitialSetupDialog = class;
67 
68   { TSearchFpcSourceThread }
69 
70   TSearchFpcSourceThread = class(TThread)
71   private
72     fSetupDialog: TInitialSetupDialog;
73     fFPCVer: string;
74     fFoundFPCSrc: TSDFileInfo;
75     {$IFDEF VerboseFPCSrcScanThead}
76     fPath: string;
77     fFileInfo: TSearchRec;
78     procedure Debug;
79     {$ENDIF}
CheckFPCSrcDirnull80     function CheckFPCSrcDir(Dir: string): TSDFileInfo;
81     procedure DoSearch(const APath: String);
82     procedure UpdateFPCSrcDir;
83     procedure Finishing;
84   protected
85     procedure Execute; override;
86   public
87     constructor Create(aSetupDialog: TInitialSetupDialog);
88     destructor Destroy; override;
89   end;
90 
91   { TInitialSetupDialog }
92 
93   TInitialSetupDialog = class(TForm)
94     BtnPanel: TPanel;
95     CompilerBrowseButton: TButton;
96     CompilerComboBox: TComboBox;
97     CompilerLabel: TLabel;
98     CompilerMemo: TMemo;
99     CompilerTabSheet: TTabSheet;
100     DebuggerBrowseButton: TButton;
101     DebuggerComboBox: TComboBox;
102     DebuggerLabel: TLabel;
103     DebuggerMemo: TMemo;
104     DebuggerTabSheet: TTabSheet;
105     FPCSourcesTabSheet: TTabSheet;
106     FPCSrcDirBrowseButton: TButton;
107     FPCSrcDirComboBox: TComboBox;
108     FPCSrcDirLabel: TLabel;
109     FPCSrcDirMemo: TMemo;
110     ImageList1: TImageList;
111     LazarusTabSheet: TTabSheet;
112     LazDirBrowseButton: TButton;
113     LazDirComboBox: TComboBox;
114     LazDirLabel: TLabel;
115     LazDirMemo: TMemo;
116     MakeExeBrowseButton: TButton;
117     MakeExeComboBox: TComboBox;
118     MakeExeLabel: TLabel;
119     MakeExeMemo: TMemo;
120     MakeExeTabSheet: TTabSheet;
121     PropertiesPageControl: TPageControl;
122     PropertiesTreeView: TTreeView;
123     ScanLabel: TLabel;
124     ScanProgressBar: TProgressBar;
125     Splitter1: TSplitter;
126     StartIDEBitBtn: TBitBtn;
127     StopScanButton: TBitBtn;
128     WelcomePaintBox: TPaintBox;
129     FppkgTabSheet: TTabSheet;
130     FppkgComboBox: TComboBox;
131     FppkgLabel: TLabel;
132     FppkgBrowseButton: TButton;
133     FppkgMemo: TMemo;
134     FppkgWriteConfigButton: TButton;
135     procedure CompilerBrowseButtonClick(Sender: TObject);
136     procedure CompilerComboBoxChange(Sender: TObject);
137     procedure CompilerComboBoxExit(Sender: TObject);
138     procedure DebuggerBrowseButtonClick(Sender: TObject);
139     procedure DebuggerComboBoxChange(Sender: TObject);
140     procedure FormCreate(Sender: TObject);
141     procedure FormDestroy(Sender: TObject);
142     procedure FPCSrcDirBrowseButtonClick(Sender: TObject);
143     procedure FPCSrcDirComboBoxChange(Sender: TObject);
144     procedure LazDirBrowseButtonClick(Sender: TObject);
145     procedure LazDirComboBoxChange(Sender: TObject);
146     procedure MakeExeBrowseButtonClick(Sender: TObject);
147     procedure MakeExeComboBoxChange(Sender: TObject);
148     procedure OnAppActivate(Sender: TObject);
149     procedure PropertiesPageControlChange(Sender: TObject);
150     procedure PropertiesTreeViewSelectionChanged(Sender: TObject);
151     procedure StartIDEBitBtnClick(Sender: TObject);
152     procedure StopScanButtonClick(Sender: TObject);
153     procedure WelcomePaintBoxPaint(Sender: TObject);
154     procedure OnIdle(Sender: TObject; var {%H-}Done: Boolean);
155     procedure FppkgComboBoxChange(Sender: TObject);
156     procedure FppkgBrowseButtonClick(Sender: TObject);
157     procedure FppkgWriteConfigButtonClick(Sender: TObject);
158   private
159     FSkipDebugger: Boolean;
160     FFlags: TSDFlags;
161     FLastParsedLazDir: string;
162     fLastParsedCompiler: string;
163     fLastParsedFPCSrcDir: string;
164     fLastParsedMakeExe: string;
165     fLastParsedDebugger: string;
166     fLastParsedFppkgConfigFile: string;
167     FIdleConnected: boolean;
168     ImgIDError: LongInt;
169     ImgIDWarning: LongInt;
170     FHeadGraphic: TPortableNetworkGraphic;
171     FInitialDebuggerFileName: String;
172     FSelectingPage: boolean;
173     FCandidates: array[TSDFilenameType] of TSDFileInfoList; // list of TSDFileInfo
174     fSearchFpcSourceThread: TSearchFpcSourceThread;
175     procedure UpdateCaptions;
176     procedure SelectPage(const NodeText: string);
SelectDirectorynull177     function SelectDirectory(aTitle: string): string;
SelectDirectorynull178     function SelectDirectory(aTitle: string; aPathFileName: string;
179                              aEnvOptParseType: TEnvOptParseType): string; overload;
180     procedure StartFPCSrcThread;
181     procedure UpdateLazarusDirCandidates;
182     procedure UpdateCompilerFilenameCandidates;
183     procedure UpdateFPCSrcDirCandidates;
184     procedure UpdateFPCSrcDirCandidate(aFPCSrcDirInfo: TSDFileInfo);
185     procedure UpdateMakeExeCandidates(aStopIfFits: boolean = False);
186     procedure UpdateDebuggerCandidates;
187     procedure UpdateFppkgCandidates;
188     procedure FillComboboxWithFileInfoList(ABox: TComboBox; List: TSDFileInfoList;
189        ItemIndex: integer = 0);
190     procedure SetIdleConnected(const AValue: boolean);
191     procedure UpdateLazDirNote;
192     procedure UpdateCompilerNote(aQuiet: boolean = False);
193     procedure UpdateFPCSrcDirNote;
194     procedure UpdateMakeExeNote;
195     procedure UpdateDebuggerNote;
196     procedure UpdateFppkgNote;
FirstErrorNodenull197     function FirstErrorNode: TTreeNode;
FirstWarningNodenull198     function FirstWarningNode: TTreeNode;
GetFirstCandidatenull199     function GetFirstCandidate(Candidates: TSDFileInfoList;
200       MinQuality: TSDFilenameQuality = sddqCompatible): TSDFileInfo;
QualityToImgIndexnull201     function QualityToImgIndex(Quality: TSDFilenameQuality): integer;
202     procedure ShowHideScanControls(aShow: Boolean);
203     procedure ThreadTerminated(Sender: TObject); // called in main thread by fSearchFpcSourceThread.OnTerminate
204     procedure TranslateResourceStrings;
205   public
206     TVNodeLazarus: TTreeNode;
207     TVNodeCompiler: TTreeNode;
208     TVNodeFPCSources: TTreeNode;
209     TVNodeMakeExe: TTreeNode;
210     TVNodeDebugger: TTreeNode;
211     TVNodeFppkg: TTreeNode;
212     procedure Init; //Check for config errors, find and show alternatives
213     property IdleConnected: boolean read FIdleConnected write SetIdleConnected;
214   end;
215 
216 
ShowInitialSetupDialognull217 function ShowInitialSetupDialog: TModalResult;
218 
219 // Debugger
220 // Checks a given file to see if it is a valid debugger (only gdb supported for now)
CheckDebuggerQualitynull221 function CheckDebuggerQuality(AFilename: string; out Note: string; ASkip: Boolean = False): TSDFilenameQuality;
222 // Search debugger candidates and add them to list, including quality level
SearchDebuggerCandidatesnull223 function SearchDebuggerCandidates(StopIfFits: boolean): TSDFileInfoList;
224 
225 implementation
226 
227 const
228   DefaultDebuggerClass: TDebuggerClass = TGDBMIDebugger;
229 
230 type
231 
232   { TSetupMacros }
233 
234   TSetupMacros = class(TTransferMacroList)
235   protected
236     procedure DoSubstitution({%H-}TheMacro: TTransferMacro; const MacroName: string;
237       var s: string; const {%H-}Data: PtrInt; var Handled, {%H-}Abort: boolean;
238       {%H-}Depth: integer); override;
239   public
240     FPCVer: string;
241     LazarusDir: string;
242   end;
243 
CheckDebuggerQualitynull244 function CheckDebuggerQuality(AFilename: string; out Note: string;
245   ASkip: Boolean): TSDFilenameQuality;
246 begin
247   Note := '';
248   Result:=sddqCompatible;
249   if ASkip and // assume compatible
250      ( (EnvironmentOptions.CurrentDebuggerPropertiesConfig = nil) or
251        (EnvironmentOptions.CurrentDebuggerPropertiesConfig.DebuggerFilename = AFilename)   // unless the user edited the filename
252      )
253   then
254     exit;
255   Result:=sddqInvalid;
256   AFilename:=TrimFilename(AFilename);
257   if not FileExistsCached(AFilename) then
258   begin
259     Note:=lisFileNotFound4;
260     exit;
261   end;
262   if DirPathExistsCached(AFilename) then
263   begin
264     Note:=lisFileIsDirectory;
265     exit;
266   end;
267   if not FileIsExecutableCached(AFilename) then
268   begin
269     Note:=lisFileIsNotAnExecutable;
270     exit;
271   end;
272 
273   { We could call gdb and parse the output looking for something like
274   GNU gdb, but that may be going too far. }
275   Note:=lisOk;
276   Result:=sddqCompatible;
277 end;
278 
SearchDebuggerCandidatesnull279 function SearchDebuggerCandidates(StopIfFits: boolean): TSDFileInfoList;
280 
CheckFilenull281   function CheckFile(AFilename: string; var List: TSDFileInfoList): boolean;
282   var
283     Item: TSDFileInfo;
284     RealFilename: String;
285   begin
286     Result:=false;
287     if AFilename='' then exit;
288     ForcePathDelims(AFilename);
289     // check if already checked
290     if Assigned(List) and List.CaptionExists(AFilename) then exit;
291     RealFilename:=EnvironmentOptions.GetParsedValue(eopDebuggerFilename, AFilename);
292     debugln(['SearchDebuggerCandidates Value=',AFilename,' File=',RealFilename]);
293     if RealFilename='' then exit;
294     // check if exists
295     if not FileExistsCached(RealFilename) then exit;
296     // add to list and check quality
297     if List=nil then
298       List:=TSDFileInfoList.create(true);
299     Item:=List.AddNewItem(RealFilename, AFilename);
300     Item.Quality:=CheckDebuggerQuality(RealFilename, Item.Note);
301     Result:=(Item.Quality=sddqCompatible) and StopIfFits;
302   end;
303 
304 const
305   DebuggerFileName='gdb'; //For Windows, .exe will be appended
306 var
307   s, AFilename, XmlClassName, CurDbgClassName: String;
308   Files: TStringList;
309   i: Integer;
310 begin
311   Result:=nil;
312 
313   // check current setting
314   if CheckFile(EnvironmentOptions.DebuggerFilename,Result) then exit;
315 
316   if EnvironmentOptions.CurrentDebuggerPropertiesConfig <> nil then
317     CurDbgClassName := UpperCase(EnvironmentOptions.CurrentDebuggerPropertiesConfig.ConfigClass)
318   else
319     CurDbgClassName := UpperCase(DefaultDebuggerClass.ClassName);
320 
321   // check the primary options
322   XmlClassName :=GetValueFromPrimaryConfig(EnvOptsConfFileName,
323                                   'EnvironmentOptions/Debugger/Class');
324   if UpperCase(XmlClassName) = CurDbgClassName then begin
325     AFilename:=GetValueFromPrimaryConfig(EnvOptsConfFileName,
326                                     'EnvironmentOptions/DebuggerFilename/Value');
327     if CheckFile(AFilename,Result) then exit;
328   end;
329 
330   // check the secondary options
331   XmlClassName :=GetValueFromSecondaryConfig(EnvOptsConfFileName,
332                                   'EnvironmentOptions/Debugger/Class');
333   if UpperCase(XmlClassName) = CurDbgClassName then begin
334     AFilename:=GetValueFromSecondaryConfig(EnvOptsConfFileName,
335                                     'EnvironmentOptions/DebuggerFilename/Value');
336     if CheckFile(AFilename,Result) then exit;
337   end;
338 
339   // Check locations proposed by debugger class
340   if EnvironmentOptions.CurrentDebuggerClass <> nil then
341     s := EnvironmentOptions.CurrentDebuggerClass.ExePaths
342   else
343     s := DefaultDebuggerClass.ExePaths;
344   while s <> '' do begin
345     AFilename := GetPart([], [';'], s);
346     if CheckFile(AFilename, Result) then exit;
347     if s <> '' then delete(s, 1, 1);
348   end;
349 
350   // Search for gdb
351   // only if TGDBMIDebugger
352   if CurDbgClassName = UpperCase(DefaultDebuggerClass.ClassName) then begin
353 
354     // Windows-only locations:
355     if (GetDefaultSrcOSForTargetOS(GetCompiledTargetOS)='win') then begin
356       // check for debugger in fpc.exe directory - could be a lucky shot
357       if CheckFile(GetForcedPathDelims('$Path($(CompPath))/'+DebuggerFileName+GetExecutableExt),Result)
358         then exit;
359     end;
360 
361     // check history
362     Files:=EnvironmentOptions.DebuggerFileHistory[CurDbgClassName];
363     if (Files=nil) or (Files.Count=0) then
364       Files:=EnvironmentOptions.DebuggerFileHistory[''];
365     if Files<>nil then
366       for i:=0 to Files.Count-1 do
367         if CheckFile(Files[i],Result) then exit;
368 
369     // check PATH
370     AFilename:=DebuggerFileName+GetExecutableExt;
371     if CheckFile(AFilename,Result) then exit;
372   end;
373 
374   // There are no common directories apart from the PATH
375   // where gdb would be installed. Otherwise we could do something similar as
376   // in SearchMakeExeCandidates.
377 end;
378 
ShowInitialSetupDialognull379 function ShowInitialSetupDialog: TModalResult;
380 var
381   InitialSetupDialog: TInitialSetupDialog;
382 begin
383   InitialSetupDialog:=TInitialSetupDialog.Create(nil);
384   try
385     Application.TaskBarBehavior:=tbMultiButton;
386     InitialSetupDialog.Init;
387     Result:=InitialSetupDialog.ShowModal;
388   finally
389     InitialSetupDialog.Free;
390     Application.TaskBarBehavior:=tbDefault;
391   end;
392 end;
393 
394 { TSearchFpcSourceThread }
395 
396 constructor TSearchFpcSourceThread.Create(aSetupDialog: TInitialSetupDialog);
397 begin
398   inherited Create(True);
399   FreeOnTerminate:=True;
400   fSetupDialog:=aSetupDialog;
401 end;
402 
403 destructor TSearchFpcSourceThread.Destroy;
404 begin
405   inherited Destroy;
406 end;
407 
408 procedure TSearchFpcSourceThread.Execute;
409 var
410   RootDir: String;
411 begin
412   // ToDo: RootDir must be changed for Windows and maybe other systems.
413   //       GetUserDir returns the user profile dir on Windows.
414   RootDir:=GetUserDir;
415   // Scan directories under root directory.
416   DoSearch(AppendPathDelim(RootDir));
417   if Assigned(fFoundFPCSrc) then
418     Synchronize(@UpdateFPCSrcDir); // Update GUI in main thread.
419   Synchronize(@Finishing);
420 end;
421 
TSearchFpcSourceThread.CheckFPCSrcDirnull422 function TSearchFpcSourceThread.CheckFPCSrcDir(Dir: string): TSDFileInfo;
423 var
424   RealDir: String;
425 begin
426   Result:=Nil;
427   RealDir:=TrimFilename(Dir);
428   if RealDir='' then exit;
429   if not DirPathExistsCached(RealDir) then exit;   // check if exists
430   Result:=TSDFileInfo.Create;
431   Result.Filename:=RealDir;
432   Result.Caption:=Dir;                             // check quality
433   Result.Quality:=CheckFPCSrcDirQuality(RealDir, Result.Note, fFPCVer, False);
434   if Result.Quality<>sddqCompatible then           // return only exact matches
435     FreeAndNil(Result);
436 end;
437 
438 procedure TSearchFpcSourceThread.DoSearch(const APath: String);
439 var
440   PathInfo: TSearchRec;
441   FPCSrc: TSDFileInfo;
442 begin
443   if FindFirstUTF8(APath+AllDirectoryEntriesMask, faDirectory, PathInfo) = 0 then
444   try
445     repeat
446       if Terminated then Break;
447       if (PathInfo.Name='') or (PathInfo.Name[1]='.')
448       or ((PathInfo.Attr and faDirectory) = 0) then Continue;
449       {$IFDEF VerboseFPCSrcScanThead}
450       fPath := APath;
451       fFileInfo := PathInfo;
452       Synchronize(@Debug);
453       {$ENDIF}
454       DoSearch(AppendPathDelim(APath+PathInfo.Name));  // Recursive call
455       FPCSrc:=CheckFPCSrcDir(APath+PathInfo.Name);
456       if Assigned(FPCSrc) then begin
457         fFoundFPCSrc:=FPCSrc;                 // An exact match was found.
458         Terminate;
459       end;
460     until (FindNextUTF8(PathInfo) <> 0);
461   finally
462     FindCloseUTF8(PathInfo);
463   end;
464 end;
465 
466 {$IFDEF VerboseFPCSrcScanThead}
467 procedure TSearchFpcSourceThread.Debug;
468 begin
469   DebugLn(['* TSearchFpcSourceThread.Debug: Path=', fPath, ', Name=', fFileInfo.Name]);
470 end;
471 {$ENDIF}
472 
473 procedure TSearchFpcSourceThread.UpdateFPCSrcDir;
474 begin
475   DebugLn(['TSearchFpcSourceThread.UpdateFPCSrcDir']);
476   fSetupDialog.UpdateFPCSrcDirCandidate(fFoundFPCSrc);
477   fSetupDialog.UpdateFPCSrcDirNote;
478 end;
479 
480 procedure TSearchFpcSourceThread.Finishing;
481 begin
482   DebugLn(['TSearchFpcSourceThread.Finishing']);
483   fSetupDialog.ShowHideScanControls(False); // Hide scan controls
484 end;
485 
486 { TSetupMacros }
487 
488 procedure TSetupMacros.DoSubstitution(TheMacro: TTransferMacro;
489   const MacroName: string; var s: string; const Data: PtrInt; var Handled,
490   Abort: boolean; Depth: integer);
491 begin
492   Handled:=true;
493   if CompareText(MacroName,'ENV')=0 then
494     s:=GetEnvironmentVariableUTF8(MacroName)
495   else if CompareText(MacroName,'PrimaryConfigPath')=0 then
496     s:=GetPrimaryConfigPath
497   else if CompareText(MacroName,'SecondaryConfigPath')=0 then
498     s:=GetSecondaryConfigPath
499   else if CompareText(MacroName,'FPCVer')=0 then begin
500     if FPCVer<>'' then
501       s:=FPCVer
502     else
503       s:={$I %FPCVERSION%};
504   end else if CompareText(MacroName,'LazarusDir')=0 then begin
505     if LazarusDir<>'' then
506       s:=LazarusDir
507     else
508       s:='<LazarusDirNotSet>';
509   end else if (CompareText(MacroName,'TargetOS')=0) then
510     s:=GetCompiledTargetOS
511   else if (CompareText(MacroName,'TargetCPU')=0) then
512     s:=GetCompiledTargetCPU
513   else if (CompareText(MacroName,'SrcOS')=0) then
514     s:=GetDefaultSrcOSForTargetOS(GetCompiledTargetOS)
515   else
516     Handled:=false;
517   //debugln(['TSetupMacros.DoSubstitution MacroName=',MacroName,' Value="',s,'"']);
518 end;
519 
520 {$R *.lfm}
521 
522 { TInitialSetupDialog }
523 
524 procedure TInitialSetupDialog.FormCreate(Sender: TObject);
525 begin
526   LazarusTabSheet.Caption:='Lazarus';
527   CompilerTabSheet.Caption:=lisCompiler;
528   FPCSourcesTabSheet.Caption:=lisFPCSources;
529   MakeExeTabSheet.Caption:='Make';
530   DebuggerTabSheet.Caption:=lisDebugger;
531   FppkgTabSheet.Caption := 'Fppkg';
532 
533   FHeadGraphic:=TPortableNetworkGraphic.Create;
534   FHeadGraphic.LoadFromResourceName(HInstance, 'ide_icon48x48');
535 
536   TVNodeLazarus:=PropertiesTreeView.Items.Add(nil,LazarusTabSheet.Caption);
537   TVNodeCompiler:=PropertiesTreeView.Items.Add(nil,CompilerTabSheet.Caption);
538   TVNodeFPCSources:=PropertiesTreeView.Items.Add(nil,FPCSourcesTabSheet.Caption);
539   TVNodeMakeExe:=PropertiesTreeView.Items.Add(nil,MakeExeTabSheet.Caption);
540   TVNodeDebugger:=PropertiesTreeView.Items.Add(nil,DebuggerTabSheet.Caption);
541   {$IF FPC_FULLVERSION>30100}
542   TVNodeFppkg:=PropertiesTreeView.Items.Add(nil,FppkgTabSheet.Caption);
543   FppkgTabSheet.TabVisible := True;
544   {$ELSE}
545   FppkgTabSheet.TabVisible := False;
546   {$ENDIF FPC_FULLVERSION>30100}
547   ImgIDError := Imagelist1.AddResourceName(HInstance, 'state_error');
548   ImgIDWarning := Imagelist1.AddResourceName(HInstance, 'state_warning');
549 
550   IDEImages.AssignImage(StopScanButton, 'menu_stop');
551 
552   UpdateCaptions;
553 
554   Application.AddOnActivateHandler(@OnAppActivate);
555 end;
556 
557 procedure TInitialSetupDialog.CompilerComboBoxChange(Sender: TObject);
558 begin
559   UpdateCompilerNote({Quiet} True);
560   UpdateFPCSrcDirNote;
561 end;
562 
563 procedure TInitialSetupDialog.CompilerComboBoxExit(Sender: TObject);
564 begin
565   UpdateCompilerNote({Quiet} False);
566   UpdateFPCSrcDirNote;
567 end;
568 
569 procedure TInitialSetupDialog.DebuggerBrowseButtonClick(Sender: TObject);
570 var
571   lExpandedName: string; // Expanded name before Dialog
572   lDirName, lFileName: string;
573   lTitle: string;
574   lChanged: boolean=False;
575   Dlg: TIDEOpenDialog;
576   Filter: String;
577 begin
578   Dlg:=IDEOpenDialogClass.Create(nil);
579   try
580     lTitle := 'gdb'+GetExecutableExt;
581     Dlg.Title := SimpleFormat(lisSelectPathTo, [lTitle]);
582     lExpandedName := EnvironmentOptions.GetParsedValue(eopDebuggerFilename, DebuggerComboBox.Text);
583     lDirName := GetValidDirectory(lExpandedName, {out} lFileName);
584     Dlg.Options := Dlg.Options+[ofFileMustExist];
585     if lFileName='' then
586       lFileName := lTitle;
587     Filter := dlgFilterAll+'|'+GetAllFilesMask;
588     if ExtractFileExt(lFileName)<>'' then
589       Filter := dlgFilterExecutable+'|*'+ExtractFileExt(lFileName)+'|'+Filter;
590     Dlg.Filter := Filter;
591     Dlg.InitialDir := lDirName;
592     Dlg.FileName := lFileName;
593     if not Dlg.Execute then
594       exit;
595     lFileName := CleanAndExpandFilename(Dlg.Filename);
596     lChanged := UpperCase(lExpandedName)<>UpperCase(lFileName);
597   finally
598     Dlg.Free;
599   end;
600   if lChanged then begin // Avoid loosing $(macros)
601     DebuggerComboBox.Text := lFileName;
602     UpdateDebuggerNote;
603   end;
604 end;
605 
606 procedure TInitialSetupDialog.DebuggerComboBoxChange(Sender: TObject);
607 begin
608   UpdateDebuggerNote;
609 end;
610 
611 procedure TInitialSetupDialog.CompilerBrowseButtonClick(Sender: TObject);
612 var
613   lExpandedName: string; // Expanded name before Dialog
614   lDirName, lFileName: string;
615   lTitle: string;
616   lChanged: boolean=False;
617   Dlg: TIDEOpenDialog;
618   Filter: String;
619 begin
620   Dlg := IDEOpenDialogClass.Create(nil);
621   try
622     lTitle := 'fpc'+GetExecutableExt;
623     Dlg.Title := SimpleFormat(lisSelectPathTo, [lTitle]);
624     lExpandedName := EnvironmentOptions.GetParsedValue(eopCompilerFilename, CompilerComboBox.Text);
625     lDirName := GetValidDirectory(lExpandedName, {out} lFileName);
626     Dlg.Options := Dlg.Options+[ofFileMustExist];
627     if lFileName='' then
628       lFileName := lTitle;
629     Filter := dlgFilterAll+'|'+GetAllFilesMask;
630     if ExtractFileExt(lFileName)<>'' then
631       Filter := dlgFilterExecutable+'|*'+ExtractFileExt(lFileName)+'|'+Filter;
632     Dlg.Filter := Filter;
633     Dlg.InitialDir := lDirName;
634     Dlg.FileName := lFileName;
635     if not Dlg.Execute then
636       exit;
637     lFileName := CleanAndExpandFilename(Dlg.Filename);
638     lChanged := UpperCase(lExpandedName)<>UpperCase(lFileName);
639   finally
640     Dlg.Free;
641   end;
642   if lChanged then begin // Avoid loosing $(macros)
643     CompilerComboBox.Text := lFileName;
644     UpdateCompilerNote;
645   end;
646 end;
647 
648 procedure TInitialSetupDialog.FormDestroy(Sender: TObject);
649 var
650   d: TSDFilenameType;
651 begin
652   IdleConnected:=false;
653   if Assigned(fSearchFpcSourceThread) then begin
654     fSearchFpcSourceThread.Terminate;
655     fSearchFpcSourceThread.WaitFor;
656   end;
657   for d:=low(FCandidates) to high(FCandidates) do
658     FreeAndNil(FCandidates[d]);
659   FreeAndNil(FHeadGraphic);
660 end;
661 
662 procedure TInitialSetupDialog.FPCSrcDirBrowseButtonClick(Sender: TObject);
663 var
664   Dir: String;
665 begin
666   Dir:=SelectDirectory(lisSelectFPCSourceDirectory, FPCSrcDirComboBox.Text,eopFPCSourceDirectory);
667   if Dir='' then
668     exit;
669   FPCSrcDirComboBox.Text:=Dir;
670   UpdateFPCSrcDirNote;
671 end;
672 
673 procedure TInitialSetupDialog.FPCSrcDirComboBoxChange(Sender: TObject);
674 begin
675   UpdateFPCSrcDirNote;
676 end;
677 
678 procedure TInitialSetupDialog.LazDirBrowseButtonClick(Sender: TObject);
679 var
680   Dir: String;
681 begin
682   Dir:=SelectDirectory(lisSelectLazarusSourceDirectory,LazDirComboBox.Text,eopLazarusDirectory);
683   if Dir='' then
684     exit;
685   LazDirComboBox.Text:=Dir;
686   UpdateLazDirNote;
687 end;
688 
689 procedure TInitialSetupDialog.LazDirComboBoxChange(Sender: TObject);
690 begin
691   UpdateLazDirNote;
692 end;
693 
694 procedure TInitialSetupDialog.MakeExeBrowseButtonClick(Sender: TObject);
695 var
696   lExpandedName: string; // Expanded name before Dialog
697   lDirName, lFileName: string;
698   lTitle: string;
699   lChanged: boolean=False;
700   Dlg: TIDEOpenDialog;
701   Filter: String;
702 
703 begin
704   Dlg := IDEOpenDialogClass.Create(nil);
705   try
706     lTitle := 'make'+GetExecutableExt;
707     Dlg.Title := SimpleFormat(lisSelectPathTo, [lTitle]);
708     lExpandedName := EnvironmentOptions.GetParsedValue(eopMakeFilename, MakeExeComboBox.Text);
709     lDirName := GetValidDirectory(lExpandedName, {out} lFileName);
710     Dlg.Options := Dlg.Options+[ofFileMustExist];
711     if lFileName='' then
712       lFileName := lTitle;
713     Filter := dlgFilterAll+'|'+GetAllFilesMask;
714     if ExtractFileExt(lFileName)<>'' then
715       Filter := dlgFilterExecutable+'|*'+ExtractFileExt(lFileName)+'|'+Filter;
716     Dlg.Filter := Filter;
717     Dlg.InitialDir := lDirName;
718     Dlg.FileName := lFileName;
719     if not Dlg.Execute then
720       exit;
721     lFileName := CleanAndExpandFilename(Dlg.Filename);
722     lChanged := UpperCase(lExpandedName)<>UpperCase(lFileName);
723   finally
724     Dlg.Free;
725   end;
726   if lChanged then begin // Avoid loosing $(macros)
727     MakeExeComboBox.Text := lFileName;
728     UpdateMakeExeNote;
729   end;
730 end;
731 
732 procedure TInitialSetupDialog.MakeExeComboBoxChange(Sender: TObject);
733 begin
734   UpdateMakeExeNote;
735 end;
736 
737 procedure TInitialSetupDialog.OnAppActivate(Sender: TObject);
738 begin
739   // switched back from another application
740   InvalidateFileStateCache;
741 end;
742 
743 procedure TInitialSetupDialog.PropertiesPageControlChange(Sender: TObject);
744 var
745   s: String;
746   i: Integer;
747 begin
748   if PropertiesPageControl.ActivePage=nil then exit;
749   s:=PropertiesPageControl.ActivePage.Caption;
750   for i:=0 to PropertiesTreeView.Items.TopLvlCount-1 do
751     if PropertiesTreeView.Items.TopLvlItems[i].Text=s then
752       PropertiesTreeView.Selected:=PropertiesTreeView.Items.TopLvlItems[i];
753 end;
754 
755 procedure TInitialSetupDialog.PropertiesTreeViewSelectionChanged(Sender: TObject);
756 begin
757   if PropertiesTreeView.Selected=nil then
758     SelectPage(TVNodeLazarus.Text)
759   else
760     SelectPage(PropertiesTreeView.Selected.Text);
761 end;
762 
763 procedure TInitialSetupDialog.StartIDEBitBtnClick(Sender: TObject);
764 var
765   Node: TTreeNode;
766   s: String;
767   MsgResult: TModalResult;
768 begin
769   Node:=FirstErrorNode;
770   s:='';
771   if Node=TVNodeLazarus then
772     s:=lisWithoutAProperLazarusDirectoryYouWillGetALotOfWarn
773   else if Node=TVNodeCompiler then
774     s:=lisWithoutAProperCompilerTheCodeBrowsingAndCompilingW
775   else if Node=TVNodeFPCSources then
776     s:=lisWithoutTheProperFPCSourcesCodeBrowsingAndCompletio
777   else if Node=TVNodeMakeExe then
778     s:=lisWithoutAProperMakeExecutableTheCompilingOfTheIDEIs
779   else if Node=TVNodeDebugger then
780     s:=lisWithoutAProperDebuggerDebuggingWillBeDisappointing;
781   if s<>'' then begin
782     MsgResult:=MessageDlg(lisCCOWarningCaption, s, mtWarning, [mbIgnore,
783       mbCancel], 0);
784     if MsgResult<>mrIgnore then exit;
785   end;
786 
787   s:=LazDirComboBox.Text;
788   if s<>'' then
789     EnvironmentOptions.LazarusDirectory:=s;
790   s:=CompilerComboBox.Text;
791   if s<>'' then
792     EnvironmentOptions.CompilerFilename:=s;
793   s:=FPCSrcDirComboBox.Text;
794   if s<>'' then
795     EnvironmentOptions.FPCSourceDirectory:=s;
796   s:=MakeExeComboBox.Text;
797   if s<>'' then
798     EnvironmentOptions.MakeFilename:=s;
799   if not (FSkipDebugger and (EnvironmentOptions.CurrentDebuggerPropertiesConfig <> nil))
800   then begin
801     s:=DebuggerComboBox.Text;
802     if s<>'' then begin
803       EnvironmentOptions.CurrentDebuggerPropertiesConfig.DebuggerFilename:=s;
804       EnvironmentOptions.SaveDebuggerPropertiesList; // Update XML
805     end;
806   end;
807 
808   ModalResult:=mrOk;
809 end;
810 
811 procedure TInitialSetupDialog.StopScanButtonClick(Sender: TObject);
812 begin
813   if fSearchFpcSourceThread<>nil then
814     fSearchFpcSourceThread.Terminate;
815 end;
816 
817 procedure TInitialSetupDialog.WelcomePaintBoxPaint(Sender: TObject);
818 begin
819   with WelcomePaintBox.Canvas do begin
820     GradientFill(WelcomePaintBox.ClientRect,$854b32,$c88e60,gdHorizontal);
821     Draw(0,WelcomePaintBox.ClientHeight-FHeadGraphic.Height,FHeadGraphic);
822     Font.Color:=clWhite;
823     Font.Height:=30;
824     Brush.Style:=bsClear;
825     TextOut(FHeadGraphic.Width+15, 5, lisConfigureLazarusIDE);
826   end;
827 end;
828 
829 procedure TInitialSetupDialog.OnIdle(Sender: TObject; var Done: Boolean);
830 begin
831   if sdfCompilerFilenameNeedsUpdate in FFlags then begin
832     UpdateCompilerFilenameCandidates;
833     UpdateCompilerNote;
834   end else if sdfFPCSrcDirNeedsUpdate in FFlags then begin
835     UpdateFPCSrcDirCandidates;
836     UpdateFPCSrcDirNote;
837   end else if sdfMakeExeFilenameNeedsUpdate in FFlags then begin
838     UpdateMakeExeCandidates;
839     UpdateMakeExeNote;
840   end else if sdfDebuggerFilenameNeedsUpdate in FFlags then begin
841     UpdateDebuggerCandidates;
842     UpdateDebuggerNote;
843   end else if sdfFppkgConfigFileNeedsUpdate in FFlags then begin
844     fLastParsedFppkgConfigFile := ' ';
845     UpdateFppkgCandidates;
846     UpdateFppkgNote;
847   end else
848     IdleConnected:=false;
849 end;
850 
851 procedure TInitialSetupDialog.UpdateCaptions;
852 var
853   s: String;
854 begin
855   Caption:=SimpleFormat(lisWelcomeToLazarusIDE, [GetLazarusVersionString]);
856 
857   StartIDEBitBtn.Caption:=lisStartIDE;
858 
859   LazarusTabSheet.Caption:='Lazarus';
860   CompilerTabSheet.Caption:=lisCompiler;
861   FPCSourcesTabSheet.Caption:=lisFPCSources;
862   MakeExeTabSheet.Caption:='Make';
863   DebuggerTabSheet.Caption:=lisDebugger;
864   FppkgTabSheet.Caption:='Fppkg';
865 
866   TVNodeLazarus.Text:=LazarusTabSheet.Caption;
867   TVNodeCompiler.Text:=CompilerTabSheet.Caption;
868   TVNodeFPCSources.Text:=FPCSourcesTabSheet.Caption;
869   TVNodeMakeExe.Text:=MakeExeTabSheet.Caption;
870   TVNodeDebugger.Text:=DebuggerTabSheet.Caption;
871   {$IF FPC_FULLVERSION>30100}
872   TVNodeFppkg.Text:=FppkgTabSheet.Caption;
873   {$ENDIF FPC_FULLVERSION>30100}
874 
875   LazDirBrowseButton.Caption:=lisPathEditBrowse;
876   LazDirLabel.Caption:=SimpleFormat(
877     lisTheLazarusDirectoryContainsTheSourcesOfTheIDEAndTh, [PathDelim]);
878 
879   FppkgLabel.Caption:=lisFppkgConfiguration;
880   FppkgBrowseButton.Caption:=lisPathEditBrowse;
881   FppkgWriteConfigButton.Caption:=lisCreateFppkgConfig;
882 
883   CompilerBrowseButton.Caption:=lisPathEditBrowse;
884   CompilerLabel.Caption:=SimpleFormat(lisTheFreePascalCompilerExecutableTypicallyHasTheName,
885     [DefineTemplates.GetDefaultCompilerFilename,
886      DefineTemplates.GetDefaultCompilerFilename(GetCompiledTargetCPU)]);
887 
888   FPCSrcDirBrowseButton.Caption:=lisPathEditBrowse;
889   FPCSrcDirLabel.Caption:=SimpleFormat(lisTheSourcesOfTheFreePascalPackagesAreRequiredForBro,
890     [GetForcedPathDelims('rtl/linux/system.pp')]);
891   ScanLabel.Caption := lisScanning;
892   StopScanButton.Caption:=lisStop;
893 
894   MakeExeBrowseButton.Caption:=lisPathEditBrowse;
895   MakeExeLabel.Caption:=SimpleFormat(
896     lisTheMakeExecutableTypicallyHasTheName, ['make'+GetExecutableExt('')]);
897 
898   DebuggerBrowseButton.Caption:=lisPathEditBrowse;
899   s:=SimpleFormat(lisTheDebuggerExecutableTypicallyHasTheNamePleaseGive, [
900     'gdb'+GetExecutableExt]);
901   {$IFDEF Windows}
902   s+=' '+lisAUsefulSettingOnWindowsSystemsIsLazarusDirMingwBin;
903   {$ENDIF}
904   DebuggerLabel.Caption:=s;
905 end;
906 
907 procedure TInitialSetupDialog.SelectPage(const NodeText: string);
908 var
909   i: Integer;
910   Node: TTreeNode;
911 begin
912   if FSelectingPage then exit;
913   FSelectingPage:=true;
914   try
915     for i:=0 to PropertiesTreeView.Items.TopLvlCount-1 do begin
916       Node:=PropertiesTreeView.Items.TopLvlItems[i];
917       if Node.Text=NodeText then begin
918         PropertiesTreeView.Selected:=Node;
919         PropertiesPageControl.ActivePageIndex:=i;
920         break;
921       end;
922     end;
923   finally
924     FSelectingPage:=false;
925   end;
926 end;
927 
TInitialSetupDialog.SelectDirectorynull928 function TInitialSetupDialog.SelectDirectory(aTitle: string): string;
929 var
930   DirDlg: TSelectDirectoryDialog;
931 begin
932   Result:='';
933   DirDlg:=TSelectDirectoryDialog.Create(nil);
934   try
935     DirDlg.Title:=aTitle;
936     DirDlg.Options:=DirDlg.Options+[ofPathMustExist,ofFileMustExist];
937     if not DirDlg.Execute then exit;
938     Result:=DirDlg.FileName;
939   finally
940     DirDlg.Free;
941   end;
942 end;
943 
TInitialSetupDialog.SelectDirectorynull944 function TInitialSetupDialog.SelectDirectory(aTitle: string;
945   aPathFileName: string; aEnvOptParseType: TEnvOptParseType): string;
946 var
947   DirDlg: TSelectDirectoryDialog;
948   lCurDirName: string;
949   lDirPath: string;
950   lDirName: string;
951 begin
952   Result := '';
953   if aPathFileName='' then
954     case aEnvOptParseType of
955       eopLazarusDirectory: lDirPath := EnvironmentOptions.GetParsedLazarusDirectory;
956       eopFPCSourceDirectory: lDirPath := EnvironmentOptions.GetParsedFPCSourceDirectory;
957     end
958   else
959     lDirPath := EnvironmentOptions.GetParsedValue(eopLazarusDirectory, aPathFileName);
960   lCurDirName := CleanAndExpandFilename(ExcludeTrailingBackSlash(lDirPath));
961   lDirPath := GetValidDirectoryAndFilename(lCurDirName, {out} lDirName);
962   { ~bk
963   if lDirName = '' then begin
964      lDirName := ExtractFileName(lDirPath);
965      lDirPath := ExtractFilePath(lDirPath);
966   end;
967   }
968   lDirPath := ExcludeTrailingBackSlash(lDirPath);
969   DirDlg := TSelectDirectoryDialog.Create(nil);
970   try
971     DirDlg.Title := aTitle;
972     DirDlg.InitialDir := lDirPath;
973     DirDlg.FileName := lDirName;
974     DirDlg.Options := DirDlg.Options + [ofPathMustExist]; // ~bk, ofFileMustExist];
975     if DirDlg.Execute then begin
976       lDirName := CleanAndExpandFilename(DirDlg.FileName);
977       if UpperCase(lCurDirName)<>UpperCase(lDirName) then
978         Result := lDirName;
979     end;
980   finally
981     DirDlg.Free;
982   end;
983 end;
984 
985 procedure TInitialSetupDialog.StartFPCSrcThread;
986 begin
987   fSearchFpcSourceThread:=TSearchFpcSourceThread.Create(Self);
988   fSearchFpcSourceThread.OnTerminate:=@ThreadTerminated;
989   fSearchFpcSourceThread.fFPCVer:=GetFPCVer;
990   ShowHideScanControls(True); // Show scan controls while thread is running
991   fSearchFpcSourceThread.Start;
992 end;
993 
994 procedure TInitialSetupDialog.UpdateLazarusDirCandidates;
995 var
996   Dirs: TSDFileInfoList;
997 begin
998   Dirs:=SearchLazarusDirectoryCandidates(false);
999   FreeAndNil(FCandidates[sddtLazarusSrcDir]);
1000   FCandidates[sddtLazarusSrcDir]:=Dirs;
1001   FillComboboxWithFileInfoList(LazDirComboBox,Dirs);
1002 end;
1003 
1004 procedure TInitialSetupDialog.UpdateCompilerFilenameCandidates;
1005 var
1006   Files: TSDFileInfoList;
1007 begin
1008   Exclude(FFlags,sdfCompilerFilenameNeedsUpdate);
1009   Files:=SearchFPCExeCandidates(false,CodeToolBoss.CompilerDefinesCache.TestFilename);
1010   FreeAndNil(FCandidates[sddtCompilerFilename]);
1011   FCandidates[sddtCompilerFilename]:=Files;
1012   FillComboboxWithFileInfoList(CompilerComboBox,Files);
1013 end;
1014 
1015 procedure TInitialSetupDialog.UpdateFPCSrcDirCandidates;
1016 var
1017   Dirs: TSDFileInfoList;
1018 begin
1019   Exclude(FFlags,sdfFPCSrcDirNeedsUpdate);
1020   Dirs:=SearchFPCSrcDirCandidates(false,GetFPCVer);
1021   FreeAndNil(FCandidates[sddtFPCSrcDir]);
1022   FCandidates[sddtFPCSrcDir]:=Dirs;
1023   FillComboboxWithFileInfoList(FPCSrcDirComboBox,Dirs);
1024 end;
1025 
1026 procedure TInitialSetupDialog.UpdateFPCSrcDirCandidate(aFPCSrcDirInfo: TSDFileInfo);
1027 var
1028   Dirs: TSDFileInfoList;
1029 begin
1030   Exclude(FFlags,sdfFPCSrcDirNeedsUpdate);
1031   FreeAndNil(FCandidates[sddtFPCSrcDir]);
1032   Dirs:=TSDFileInfoList.Create;
1033   Dirs.Add(aFPCSrcDirInfo);
1034   FCandidates[sddtFPCSrcDir]:=Dirs;
1035   FillComboboxWithFileInfoList(FPCSrcDirComboBox,Dirs);
1036 end;
1037 
1038 procedure TInitialSetupDialog.UpdateMakeExeCandidates(aStopIfFits: boolean);
1039 var
1040   Files: TSDFileInfoList;
1041 begin
1042   Exclude(FFlags,sdfMakeExeFilenameNeedsUpdate);
1043   Files:=SearchMakeExeCandidates(aStopIfFits);
1044   FreeAndNil(FCandidates[sddtMakeExeFileName]);
1045   FCandidates[sddtMakeExeFileName]:=Files;
1046   FillComboboxWithFileInfoList(MakeExeComboBox,Files);
1047 end;
1048 
1049 procedure TInitialSetupDialog.UpdateDebuggerCandidates;
1050 var
1051   Files: TSDFileInfoList;
1052 begin
1053   Exclude(FFlags,sdfDebuggerFilenameNeedsUpdate);
1054   Files:=SearchDebuggerCandidates(false);
1055   FreeAndNil(FCandidates[sddtDebuggerFilename]);
1056   FCandidates[sddtDebuggerFilename]:=Files;
1057   FillComboboxWithFileInfoList(DebuggerComboBox,Files);
1058 end;
1059 
1060 procedure TInitialSetupDialog.FillComboboxWithFileInfoList(ABox: TComboBox;
1061   List: TSDFileInfoList; ItemIndex: integer);
1062 var
1063   sl: TStringList;
1064   i: Integer;
1065 begin
1066   sl:=TStringList.Create;
1067   try
1068     if List<>nil then
1069       for i:=0 to List.Count-1 do
1070         sl.Add(TSDFileInfo(List[i]).Caption);
1071     ABox.Items.Assign(sl);
1072     if (ItemIndex>=0) and (ItemIndex<sl.Count) then
1073       ABox.Text:=sl[ItemIndex]
1074     else if ABox.Text=ABox.Name then
1075       ABox.Text:='';
1076   finally
1077     sl.Free;
1078   end;
1079 end;
1080 
1081 procedure TInitialSetupDialog.SetIdleConnected(const AValue: boolean);
1082 begin
1083   if FIdleConnected=AValue then exit;
1084   FIdleConnected:=AValue;
1085   if IdleConnected then
1086     Application.AddOnIdleHandler(@OnIdle)
1087   else
1088     Application.RemoveOnIdleHandler(@OnIdle);
1089 end;
1090 
1091 procedure TInitialSetupDialog.UpdateLazDirNote;
1092 var
1093   CurCaption: String;
1094   Note: string;
1095   Quality: TSDFilenameQuality;
1096   s: String;
1097   ImageIndex: Integer;
1098 begin
1099   if csDestroying in ComponentState then exit;
1100   CurCaption:=LazDirComboBox.Text;
1101   CurCaption:=ChompPathDelim(CurCaption);
1102   EnvironmentOptions.LazarusDirectory:=CurCaption;
1103   if FLastParsedLazDir=EnvironmentOptions.GetParsedLazarusDirectory then exit;
1104   FLastParsedLazDir:=EnvironmentOptions.GetParsedLazarusDirectory;
1105   //debugln(['TInitialSetupDialog.UpdateLazDirNote ',FLastParsedLazDir]);
1106   Quality:=CheckLazarusDirectoryQuality(FLastParsedLazDir,Note);
1107   case Quality of
1108   sddqInvalid: s:=lisError;
1109   sddqCompatible: s:='';
1110   else s:=lisWarning;
1111   end;
1112   if EnvironmentOptions.LazarusDirectory<>EnvironmentOptions.GetParsedLazarusDirectory
1113   then
1114     s:=lisDirectory+EnvironmentOptions.GetParsedLazarusDirectory+LineEnding+
1115       LineEnding+s;
1116   LazDirMemo.Text:=s+Note;
1117 
1118   ImageIndex:=QualityToImgIndex(Quality);
1119   TVNodeLazarus.ImageIndex:=ImageIndex;
1120   TVNodeLazarus.SelectedIndex:=ImageIndex;
1121 
1122   FFlags:=FFlags+[sdfCompilerFilenameNeedsUpdate,sdfFPCSrcDirNeedsUpdate,
1123                   sdfMakeExeFilenameNeedsUpdate,sdfDebuggerFilenameNeedsUpdate];
1124   IdleConnected:=true;
1125 end;
1126 
1127 procedure TInitialSetupDialog.UpdateCompilerNote(aQuiet: boolean);
1128 var
1129   CurCaption, ParsedC, Note, s: String;
1130   Quality: TSDFilenameQuality;
1131   ImageIndex: Integer;
1132   CfgCache: TPCTargetConfigCache;
1133 begin
1134   if csDestroying in ComponentState then exit;
1135   CurCaption:=CompilerComboBox.Text;
1136   EnvironmentOptions.CompilerFilename:=CurCaption;
1137   ParsedC:=EnvironmentOptions.GetParsedCompilerFilename;
1138   if fLastParsedCompiler=ParsedC then exit;
1139   fLastParsedCompiler:=ParsedC;
1140   Quality:=CheckFPCExeQuality(fLastParsedCompiler,Note,
1141                               CodeToolBoss.CompilerDefinesCache.TestFilename);
1142   if Quality=sddqCompatible then
1143   begin
1144     // check compiler again
1145     CfgCache:=CodeToolBoss.CompilerDefinesCache.ConfigCaches.Find(
1146                                                fLastParsedCompiler,'','','',true);
1147     CfgCache.CompilerDate:=0; // force update
1148     if CfgCache.NeedsUpdate then
1149       CfgCache.Update(CodeToolBoss.CompilerDefinesCache.TestFilename);
1150     BuildBoss.SetBuildTargetIDE(aQuiet);
1151   end;
1152   case Quality of
1153   sddqInvalid: s:=lisError;
1154   sddqCompatible: s:='';
1155   else s:=lisWarning;
1156   end;
1157   ParsedC:=EnvironmentOptions.GetParsedCompilerFilename;
1158   if EnvironmentOptions.CompilerFilename<>ParsedC then
1159     s:=lisFile2+ParsedC+LineEnding+LineEnding+s;
1160   CompilerMemo.Text:=s+Note;
1161 
1162   ImageIndex:=QualityToImgIndex(Quality);
1163   TVNodeCompiler.ImageIndex:=ImageIndex;
1164   TVNodeCompiler.SelectedIndex:=ImageIndex;
1165 
1166   FFlags:=FFlags+[sdfFPCSrcDirNeedsUpdate,sdfFppkgConfigFileNeedsUpdate,
1167                   sdfMakeExeFilenameNeedsUpdate,sdfDebuggerFilenameNeedsUpdate];
1168   IdleConnected:=true;
1169 end;
1170 
1171 procedure TInitialSetupDialog.UpdateFPCSrcDirNote;
1172 var
1173   CurCaption: String;
1174   Note: string;
1175   Quality: TSDFilenameQuality;
1176   s: String;
1177   ImageIndex: Integer;
1178 begin
1179   if csDestroying in ComponentState then exit;
1180   CurCaption:=FPCSrcDirComboBox.Text;
1181   CurCaption:=ChompPathDelim(CurCaption);
1182   EnvironmentOptions.FPCSourceDirectory:=CurCaption;
1183   if fLastParsedFPCSrcDir=EnvironmentOptions.GetParsedFPCSourceDirectory then exit;
1184   fLastParsedFPCSrcDir:=EnvironmentOptions.GetParsedFPCSourceDirectory;
1185   //debugln(['TInitialSetupDialog.UpdateFPCSrcDirNote ',fLastParsedFPCSrcDir]);
1186   Quality:=CheckFPCSrcDirQuality(fLastParsedFPCSrcDir,Note,GetFPCVer);
1187   case Quality of
1188   sddqInvalid: s:=lisError;
1189   sddqCompatible: s:='';
1190   else s:=lisWarning;
1191   end;
1192   if EnvironmentOptions.FPCSourceDirectory<>EnvironmentOptions.GetParsedFPCSourceDirectory
1193   then
1194     s:=lisDirectory+EnvironmentOptions.GetParsedFPCSourceDirectory+LineEnding+
1195       LineEnding+s;
1196   s+=Note;
1197   if Quality<>sddqCompatible then
1198     s+=#13+lisYouCanDownloadFPCAndTheFPCSourcesFromHttpSourcefor;
1199   FPCSrcDirMemo.Text:=s;
1200 
1201   ImageIndex:=QualityToImgIndex(Quality);
1202   TVNodeFPCSources.ImageIndex:=ImageIndex;
1203   TVNodeFPCSources.SelectedIndex:=ImageIndex;
1204 end;
1205 
1206 procedure TInitialSetupDialog.UpdateMakeExeNote;
1207 var
1208   CurCaption: String;
1209   Note: string;
1210   Quality: TSDFilenameQuality;
1211   s: String;
1212   ImageIndex: Integer;
1213 begin
1214   if csDestroying in ComponentState then exit;
1215   CurCaption:=MakeExeComboBox.Text;
1216   EnvironmentOptions.MakeFilename:=CurCaption;
1217   if fLastParsedMakeExe=EnvironmentOptions.GetParsedMakeFilename then exit;
1218   fLastParsedMakeExe:=EnvironmentOptions.GetParsedMakeFilename;
1219   //debugln(['TInitialSetupDialog.UpdateMakeExeNote ',fLastParsedMakeExe]);
1220   Quality:=CheckMakeExeQuality(fLastParsedMakeExe,Note);
1221 
1222   case Quality of
1223   sddqInvalid: s:=lisError;
1224   sddqCompatible: s:='';
1225   else s:=lisWarning;
1226   end;
1227   if EnvironmentOptions.MakeFilename<>EnvironmentOptions.GetParsedMakeFilename
1228   then
1229     s:=lisFile2+EnvironmentOptions.GetParsedMakeFilename+LineEnding+
1230       LineEnding+s;
1231   MakeExeMemo.Text:=s+Note;
1232 
1233   ImageIndex:=QualityToImgIndex(Quality);
1234   TVNodeMakeExe.ImageIndex:=ImageIndex;
1235   TVNodeMakeExe.SelectedIndex:=ImageIndex;
1236 
1237   IdleConnected:=true;
1238 end;
1239 
1240 procedure TInitialSetupDialog.UpdateDebuggerNote;
1241 var
1242   CurCaption: String;
1243   Note: string;
1244   Quality: TSDFilenameQuality;
1245   s, ParsedFName: String;
1246   ImageIndex: Integer;
1247 begin
1248   if csDestroying in ComponentState then exit;
1249   CurCaption:=DebuggerComboBox.Text;
1250   ParsedFName := EnvironmentOptions.GetParsedValue(eopDebuggerFilename, CurCaption);
1251   if fLastParsedDebugger=ParsedFName then exit;
1252   fLastParsedDebugger:=ParsedFName;
1253   //debugln(['TInitialSetupDialog.UpdateDebuggerNote ',fLastParsedDebugger]);
1254   Quality:=CheckDebuggerQuality(fLastParsedDebugger,Note, FSkipDebugger);
1255 
1256   case Quality of
1257   sddqInvalid: s:=lisError;
1258   sddqCompatible: s:='';
1259   else s:=lisWarning;
1260   end;
1261   if CurCaption<>ParsedFName
1262   then
1263     s:=lisFile2+ParsedFName+LineEnding+
1264       LineEnding+s;
1265   DebuggerMemo.Text:=s+Note;
1266 
1267   ImageIndex:=QualityToImgIndex(Quality);
1268   TVNodeDebugger.ImageIndex:=ImageIndex;
1269   TVNodeDebugger.SelectedIndex:=ImageIndex;
1270 
1271   IdleConnected:=true;
1272 end;
1273 
FirstErrorNodenull1274 function TInitialSetupDialog.FirstErrorNode: TTreeNode;
1275 var
1276   i: Integer;
1277 begin
1278   for i:=0 to PropertiesTreeView.Items.TopLvlCount-1 do
1279   begin
1280     Result:=PropertiesTreeView.Items.TopLvlItems[i];
1281     if Result.ImageIndex=ImgIDError then exit;
1282   end;
1283   Result:=FirstWarningNode;
1284 end;
1285 
FirstWarningNodenull1286 function TInitialSetupDialog.FirstWarningNode: TTreeNode;
1287 var
1288   i: Integer;
1289 begin
1290   for i:=0 to PropertiesTreeView.Items.TopLvlCount-1 do
1291   begin
1292     Result:=PropertiesTreeView.Items.TopLvlItems[i];
1293     if Result.ImageIndex=ImgIDWarning then
1294       exit;
1295   end;
1296   Result:=nil;
1297 end;
1298 
GetFirstCandidatenull1299 function TInitialSetupDialog.GetFirstCandidate(Candidates: TSDFileInfoList;
1300   MinQuality: TSDFilenameQuality): TSDFileInfo;
1301 var
1302   i: Integer;
1303 begin
1304   if Candidates<>nil then
1305     for i:=0 to Candidates.Count-1 do begin
1306       Result:=TSDFileInfo(Candidates[i]);
1307       if Result.Quality>=MinQuality then
1308         exit;
1309     end;
1310   Result:=nil;
1311 end;
1312 
QualityToImgIndexnull1313 function TInitialSetupDialog.QualityToImgIndex(Quality: TSDFilenameQuality): integer;
1314 begin
1315   if Quality=sddqCompatible then
1316     Result:=-1
1317   else if Quality=sddqWrongMinorVersion then
1318     Result:=ImgIDWarning
1319   else if Quality=sddqIncomplete then
1320     Result:=ImgIDWarning
1321   else if Quality=sddqMakeNotWithFpc then
1322     Result:=ImgIDWarning
1323   else
1324     Result:=ImgIDError;
1325 end;
1326 
1327 procedure TInitialSetupDialog.ShowHideScanControls(aShow: Boolean);
1328 begin
1329   // Show ProgressBar and Stop button durin scanning
1330   ScanLabel.Visible:=aShow;
1331   ScanProgressBar.Visible:=aShow;
1332   StopScanButton.Visible:=aShow;
1333   // At the same time disable other GUI controls so a user can not mess with it
1334   StartIDEBitBtn.Enabled:=not aShow;
1335   FPCSrcDirBrowseButton.Enabled:=not aShow;
1336   FPCSrcDirComboBox.Enabled:=not aShow;
1337 //  FPCSrcDirMemo.Enabled:=not aShow;
1338 end;
1339 
1340 procedure TInitialSetupDialog.ThreadTerminated(Sender: TObject);
1341 begin
1342   debugln(['TInitialSetupDialog.ThreadTerminated ']);
1343   fSearchFpcSourceThread:=nil; // Thread will free itself. Make the variable nil, too.
1344   ShowHideScanControls(false);
1345 end;
1346 
1347 procedure TInitialSetupDialog.TranslateResourceStrings;
1348 begin
1349   IDETranslations.TranslateResourceStrings(
1350                            EnvironmentOptions.GetParsedLazarusDirectory,
1351                            EnvironmentOptions.LanguageID);
1352   UpdateCaptions;
1353 end;
1354 
1355 procedure TInitialSetupDialog.Init;
1356 var
1357   Node: TTreeNode;
1358   Candidate: TSDFileInfo;
1359   IsFirstStart: Boolean;
1360   PrimaryFilename: String;
1361   SecondaryFilename: String;
1362   PrimaryEnvs: TStringList;
1363   SecondaryEnvs: TStringList;
1364 begin
1365   IsFirstStart:=not FileExistsCached(EnvironmentOptions.Filename);
1366   if not IsFirstStart then begin
1367     IsFirstStart:= False;
1368     PrimaryFilename:=EnvironmentOptions.Filename;
1369     SecondaryFilename:=AppendPathDelim(GetSecondaryConfigPath)+ExtractFilename(PrimaryFilename);
1370     if FileExistsUTF8(PrimaryFilename)
1371     and FileExistsUTF8(SecondaryFilename) then begin
1372       // compare content of primary and secondary config
1373       PrimaryEnvs:=TStringList.Create;
1374       SecondaryEnvs:=TStringList.Create;
1375       try
1376         PrimaryEnvs.LoadFromFile(PrimaryFilename);
1377       except
1378         on E: Exception do
1379           debugln(['TInitialSetupDialog.Init unable to read "'+PrimaryFilename+'": '+E.Message]);
1380       end;
1381       try
1382         SecondaryEnvs.LoadFromFile(SecondaryFilename);
1383       except
1384         on E: Exception do
1385           debugln(['TInitialSetupDialog.Init unable to read "'+SecondaryFilename+'": '+E.Message]);
1386       end;
1387       // IsFirstStart:=PrimaryEnvs.Text=SecondaryEnvs.Text;
1388       PrimaryEnvs.Free;
1389       SecondaryEnvs.Free;
1390     end;
1391   end
1392   else
1393     IsFirstStart := True;
1394   //debugln(['TInitialSetupDialog.Init IsFirstStart=',IsFirstStart,' ',EnvironmentOptions.Filename]);
1395 
1396   // Lazarus directory
1397   UpdateLazarusDirCandidates;
1398   if IsFirstStart
1399   or (not FileExistsCached(EnvironmentOptions.GetParsedLazarusDirectory))
1400   then begin
1401     // first start => choose first best candidate
1402     Candidate:=GetFirstCandidate(FCandidates[sddtLazarusSrcDir]);
1403     if Candidate<>nil then
1404     begin
1405       EnvironmentOptions.LazarusDirectory:=Candidate.Caption;
1406       if Candidate.Quality=sddqCompatible then
1407         TranslateResourceStrings;
1408     end;
1409   end;
1410   LazDirComboBox.Text:=EnvironmentOptions.LazarusDirectory;
1411   FLastParsedLazDir:='. .';
1412   UpdateLazDirNote;
1413 
1414   // compiler filename
1415   UpdateCompilerFilenameCandidates;
1416   if IsFirstStart
1417   or (EnvironmentOptions.CompilerFilename='')
1418   or (not FileExistsCached(EnvironmentOptions.GetParsedCompilerFilename))
1419   then begin
1420     // first start => choose first best candidate
1421     Candidate:=GetFirstCandidate(FCandidates[sddtCompilerFilename]);
1422     if Candidate<>nil then
1423       EnvironmentOptions.CompilerFilename:=Candidate.Caption;
1424   end;
1425   CompilerComboBox.Text:=EnvironmentOptions.CompilerFilename;
1426   fLastParsedCompiler:='. .';
1427   UpdateCompilerNote;
1428 
1429   // FPC source directory
1430   UpdateFPCSrcDirCandidates;
1431   {$IFDEF DebugSearchFPCSrcThread}
1432   IsFirstStart:=true;
1433   {$ENDIF}
1434   if IsFirstStart or (EnvironmentOptions.FPCSourceDirectory='')
1435   or (not FileExistsCached(EnvironmentOptions.GetParsedFPCSourceDirectory))
1436   then begin
1437     // first start => choose first best candidate
1438     {$IFDEF DebugSearchFPCSrcThread}
1439     Candidate:=nil;
1440     {$ELSE}
1441     Candidate:=GetFirstCandidate(FCandidates[sddtFPCSrcDir]);
1442     {$ENDIF}
1443     if Candidate<>nil then begin
1444       EnvironmentOptions.FPCSourceDirectory:=Candidate.Caption;
1445     end
1446     else begin
1447       // No candidates found => start a thread to scan the file system.
1448       {$IFNDEF LCLCarbon}
1449       // carbon interface does not support Synchronize outside Application.Run
1450       StartFPCSrcThread;
1451       SelectPage(TVNodeFPCSources.Text);
1452       {$ENDIF}
1453     end;
1454   end;
1455   ShowHideScanControls(fSearchFpcSourceThread<>nil);
1456   FPCSrcDirComboBox.Text:=EnvironmentOptions.FPCSourceDirectory;
1457   fLastParsedFPCSrcDir:='. .';
1458   UpdateFPCSrcDirNote;
1459 
1460   // Make executable
1461   UpdateMakeExeCandidates({aStopIfFits} True);
1462   if IsFirstStart
1463   or (EnvironmentOptions.MakeFilename='')
1464   or (not FileExistsCached(EnvironmentOptions.GetParsedMakeFilename)) then
1465   begin
1466     // first start => choose first best candidate
1467     Candidate:=GetFirstCandidate(FCandidates[sddtMakeExeFilename]);
1468     if Candidate<>nil then
1469       EnvironmentOptions.MakeFilename:=Candidate.Caption
1470     else begin // second chance => better an incomplete instead of none (especially for windows)
1471       Candidate:=GetFirstCandidate(FCandidates[sddtMakeExeFilename], sddqIncomplete);
1472       if Candidate<>nil then
1473         EnvironmentOptions.MakeFilename:=Candidate.Caption;
1474     end;
1475   end;
1476   MakeExeComboBox.Text:=EnvironmentOptions.MakeFilename;
1477   fLastParsedMakeExe:='. .';
1478   UpdateMakeExeNote;
1479 
1480   RegisterDebugger(TGDBMIDebugger); // make sure we can read the config
1481   FSkipDebugger := EnvironmentOptions.HasActiveDebuggerEntry // There is a configured entry. Assume it is right, unless we can prove it is incorrect
1482     and not (
1483       (EnvironmentOptions.CurrentDebuggerPropertiesConfig <> nil) and    // The ACTIVE dbg is a known debugger
1484       (EnvironmentOptions.CurrentDebuggerClass <> nil)     and           // with existing debugger class
1485       (EnvironmentOptions.CurrentDebuggerPropertiesConfig.NeedsExePath)  // Does need an exe
1486     );
1487   // Debugger
1488   FInitialDebuggerFileName := EnvironmentOptions.DebuggerFilename;
1489   UpdateDebuggerCandidates;
1490   if (not FSkipDebugger) then begin
1491     if EnvironmentOptions.CurrentDebuggerPropertiesConfig = nil then
1492       EnvironmentOptions.CurrentDebuggerPropertiesConfig :=
1493         EnvironmentOptions.DebuggerPropertiesConfigList.EntryByName('', TGDBMIDebugger.ClassName);
1494     if EnvironmentOptions.CurrentDebuggerPropertiesConfig = nil then
1495       EnvironmentOptions.CurrentDebuggerPropertiesConfig :=
1496         TDebuggerPropertiesConfig.CreateForDebuggerClass(TGDBMIDebugger);
1497     if IsFirstStart or (not FileExistsCached(EnvironmentOptions.GetParsedDebuggerFilename))
1498     then begin
1499       // first start => choose first best candidate
1500       Candidate:=GetFirstCandidate(FCandidates[sddtDebuggerFilename]);
1501       if Candidate<>nil then begin
1502         EnvironmentOptions.CurrentDebuggerPropertiesConfig.DebuggerFilename:=Candidate.Caption;
1503       end;
1504     end;
1505     EnvironmentOptions.SaveDebuggerPropertiesList; // Update XML
1506   end;
1507 
1508   DebuggerComboBox.Text:=EnvironmentOptions.DebuggerFilename;
1509   fLastParsedDebugger:='. .';
1510   UpdateDebuggerNote;
1511 
1512   // Fppkg
1513   FppkgComboBox.Text := EnvironmentOptions.FppkgConfigFile;
1514   fLastParsedFppkgConfigFile := ' ';
1515   UpdateFppkgCandidates;
1516   UpdateFppkgNote;
1517 
1518   // select first error
1519   Node:=FirstErrorNode;
1520   if Node=nil then
1521     Node:=TVNodeLazarus;
1522   PropertiesTreeView.Selected:=Node;
1523 end;
1524 
1525 procedure TInitialSetupDialog.UpdateFppkgNote;
1526 var
1527   CurCaption: String;
1528   FppkgMsg, Note: string;
1529   Quality: TSDFilenameQuality;
1530   ConfigFile: string;
1531   {$IF FPC_FULLVERSION>30100}
1532   ImageIndex: Integer;
1533   {$ENDIF}
1534 begin
1535   if csDestroying in ComponentState then exit;
1536   CurCaption:=FppkgComboBox.Text;
1537   EnvironmentOptions.FppkgConfigFile:=CurCaption;
1538   if fLastParsedFppkgConfigFile=EnvironmentOptions.GetParsedFppkgConfig then exit;
1539   fLastParsedFppkgConfigFile:=EnvironmentOptions.GetParsedFppkgConfig;
1540 
1541   ConfigFile := fLastParsedFppkgConfigFile;
1542   Quality := CheckFppkgConfigFile(ConfigFile, Note);
1543   if Quality<>sddqCompatible then
1544   begin
1545     Note := lisError + Note + LineEnding;
1546     FppkgWriteConfigButton.Enabled := (Quality=sddqIncomplete);
1547   end
1548   else
1549   begin
1550     Quality := CheckFppkgConfiguration(ConfigFile, FppkgMsg);
1551 
1552     if Quality=sddqCompatible then
1553     begin
1554       Note := lisOk;
1555       FppkgWriteConfigButton.Enabled := False;
1556     end
1557     else
1558     begin
1559       Note := lisError + Format(lisIncorrectFppkgConfiguration, [FppkgMsg]) + LineEnding;
1560       Note := Note + LineEnding + lisFppkgFixConfiguration;
1561       FppkgWriteConfigButton.Enabled := True;
1562     end;
1563   end;
1564 
1565   FppkgMemo.Text := lisFile2 + ConfigFile + LineEnding + LineEnding + Note;
1566 
1567   {$IF FPC_FULLVERSION>30100}
1568   ImageIndex:=QualityToImgIndex(Quality);
1569   TVNodeFppkg.ImageIndex:=ImageIndex;
1570   TVNodeFppkg.SelectedIndex:=ImageIndex;
1571   {$ENDIF FPC_FULLVERSION>30100}
1572 
1573   IdleConnected:=true;
1574 end;
1575 
1576 procedure TInitialSetupDialog.FppkgComboBoxChange(Sender: TObject);
1577 begin
1578   UpdateFppkgNote;
1579 end;
1580 
1581 procedure TInitialSetupDialog.UpdateFppkgCandidates;
1582 
SearchFppkgFpcPrefixCandidatesnull1583   function SearchFppkgFpcPrefixCandidates: TSDFileInfoList;
1584 
CheckFilenull1585     function CheckFile(AFile: string; var List: TSDFileInfoList): boolean;
1586     var
1587       Item: TSDFileInfo;
1588     begin
1589       Result:=false;
1590       if AFile='' then exit;
1591       ForcePathDelims(AFile);
1592       // check if already checked
1593       if Assigned(List) and List.CaptionExists(AFile) then exit;
1594 
1595       if FileExistsUTF8(AFile) then
1596       begin
1597         if List=nil then
1598           List:=TSDFileInfoList.create(true);
1599         Item:=List.AddNewItem(AFile, AFile);
1600         Item.Quality:=sddqCompatible;
1601         Result := True;
1602       end;
1603     end;
1604   begin
1605     Result:=nil;
1606 
1607     {$IF FPC_FULLVERSION>30100}
1608     CheckFile(GetFppkgConfigFile(False, False), Result);
1609     CheckFile(GetFppkgConfigFile(False, True), Result);
1610     CheckFile(GetFppkgConfigFile(True, False), Result);
1611     CheckFile(GetFppkgConfigFile(True, True), Result);
1612     {$ENDIF}
1613   end;
1614 
1615 var
1616   Files: TSDFileInfoList;
1617 begin
1618   Exclude(FFlags,sdfFppkgConfigFileNeedsUpdate);
1619   Files:=SearchFppkgFpcPrefixCandidates;
1620   FreeAndNil(FCandidates[sddtFppkgFpcPrefix]);
1621   FCandidates[sddtFppkgFpcPrefix]:=Files;
1622   FillComboboxWithFileInfoList(FppkgComboBox,Files,-1);
1623 end;
1624 
1625 
1626 procedure TInitialSetupDialog.FppkgBrowseButtonClick(Sender: TObject);
1627 var
1628   lExpandedName: string; // Expanded name before Dialog
1629   lDirName, lFileName: string;
1630   lTitle: string;
1631   lChanged: boolean=False;
1632   Dlg: TIDEOpenDialog;
1633   Filter: String;
1634 begin
1635   Dlg:=IDEOpenDialogClass.Create(nil);
1636   try
1637     lTitle:='fppkg.cfg';
1638     Dlg.Title:=SimpleFormat(lisSelectPathTo, [lTitle]);
1639     lExpandedName:=EnvironmentOptions.GetParsedValue(eopFppkgConfigFile, FppkgComboBox.Text);
1640     lDirName := GetValidDirectory(lExpandedName, {out} lFileName);
1641     Dlg.Options:=Dlg.Options+[ofFileMustExist];
1642     if lFileName='' then
1643       lFileName:=lTitle;
1644     Filter:=dlgFilterAll+'|'+GetAllFilesMask;
1645     if ExtractFileExt(lFileName)<>'' then
1646       Filter:=dlgFilterExecutable+'|*'+ExtractFileExt(lFileName)+'|'+Filter;
1647     Dlg.Filter:=Filter;
1648     Dlg.InitialDir:=lDirName;
1649     Dlg.FileName:=lFileName;
1650     if not Dlg.Execute then
1651       exit;
1652     lFileName:=CleanAndExpandFilename(Dlg.Filename);
1653     lChanged := UpperCase(lExpandedName)<>UpperCase(lFileName);
1654   finally
1655     Dlg.Free;
1656   end;
1657   if lChanged then begin // Avoid loosing $(macros)
1658     FppkgComboBox.Text:=lFileName;
1659     UpdateFppkgNote;
1660   end;
1661 end;
1662 
1663 procedure TInitialSetupDialog.FppkgWriteConfigButtonClick(Sender: TObject);
1664 {$IF FPC_FULLVERSION>30100}
1665 var
1666   Dialog: TGenerateFppkgConfigurationDialog;
1667 {$ENDIF}
1668 begin
1669   {$IF FPC_FULLVERSION>30100}
1670   Dialog := TGenerateFppkgConfigurationDialog.Create(Self);
1671   try
1672     Dialog.Compiler := fLastParsedCompiler;
1673     if fLastParsedFppkgConfigFile='' then
1674       Dialog.FppkgCfgFilename := GetFppkgConfigFile(False, False)
1675     else
1676       Dialog.FppkgCfgFilename := fLastParsedFppkgConfigFile;
1677     Dialog.ShowModal;
1678   finally
1679     Dialog.Free;
1680   end;
1681 
1682   fLastParsedFppkgConfigFile := ' ';
1683   UpdateFppkgNote;
1684   {$ENDIF}
1685 end;
1686 
1687 end.
1688 
1689