1 unit compiler_path_options;
2 
3 {$mode objfpc}{$H+}
4 
5 interface
6 
7 uses
8   Classes, SysUtils,
9   // LCL
10   LCLProc, LCLType,Controls, Dialogs, Buttons, StdCtrls,
11   // LazUtils
12   LazFileUtils, LazFileCache,
13   // IdeIntf
14   IDEOptionsIntf, IDEOptEditorIntf, MacroIntf, CompOptsIntf, IDEImagesIntf, IDEDialogs,
15   // IDE
16   Project, CompilerOptions, LazarusIDEStrConsts, PathEditorDlg, IDEProcs,
17   CheckCompilerOpts, ShowCompilerOpts, ImExportCompilerOpts;
18 
19 type
20 
21   { TCompilerPathOptionsFrame }
22 
23   TCompilerPathOptionsFrame = class(TAbstractIDEOptionsEditor)
24     DebugPathEdit: TEdit;
25     DebugPathLabel: TLabel;
26     IncludeFilesEdit: TEdit;
27     IncludeFilesLabel: TLabel;
28     LibrariesEdit: TEdit;
29     LibrariesLabel: TLabel;
30     OtherSourcesEdit: TEdit;
31     OtherSourcesLabel: TLabel;
32     OtherUnitsEdit: TEdit;
33     OtherUnitsLabel: TLabel;
34     ProjTargetApplyConventionsCheckBox: TCheckBox;
35     ProjTargetFileEdit: TEdit;
36     ProjTargetFileLabel: TLabel;
37     UnitOutputDirEdit: TEdit;
38     UnitOutputDirLabel: TLabel;
39     procedure ProjTargetFileEditChange(Sender: TObject);
40   private
41     FDialog: TAbstractOptionsEditorDialog;
42     FCompilerOpts: TBaseCompilerOptions;
43     FHasProjectCompilerOpts: boolean;
44     OtherUnitsPathEditBtn: TPathEditorButton;
45     IncludeFilesPathEditBtn: TPathEditorButton;
46     OtherSourcesPathEditBtn: TPathEditorButton;
47     LibrariesPathEditBtn: TPathEditorButton;
48     btnUnitOutputDir: TButton;
49     DebugPathEditBtn: TPathEditorButton;
50     btnShowOptions: TBitBtn;
51     btnCheck: TBitBtn;
52     btnLoadSave: TBitBtn;
53     btnExport: TBitBtn;
54     chkUseAsDefault: TCheckBox;
CheckSrcPathInUnitPathnull55     function CheckSrcPathInUnitPath(OldParsedSrcPath, NewParsedSrcPath,
56       OldParsedUnitPath, NewParsedUnitPath: string;
57       out SrcPathChanged: boolean): boolean;
58     procedure FileBrowseBtnClick(Sender: TObject);
59     procedure PathEditBtnClick(Sender: TObject);
PathEditBtnExecutednull60     function PathEditBtnExecuted(Context: String; var NewPath: String): Boolean;
61     procedure DoShowOptions(Sender: TObject);
62     procedure DoCheck(Sender: TObject);
63     procedure DoImport(Sender: TObject);
64     procedure DoExport(Sender: TObject);
65   protected
66     procedure DoSaveSettings(AOptions: TAbstractIDEOptions);
67     procedure UpdateTargetFileLabel;
68   public
69     constructor Create(TheOwner: TComponent); override;
Checknull70     function Check: boolean; override;
GetTitlenull71     function GetTitle: string; override;
72     procedure Setup(ADialog: TAbstractOptionsEditorDialog); override;
73     procedure ReadSettings(AOptions: TAbstractIDEOptions); override;
74     procedure WriteSettings(AOptions: TAbstractIDEOptions); override;
SupportedOptionsClassnull75     class function SupportedOptionsClass: TAbstractIDEOptionsClass; override;
76   end;
77 
78 implementation
79 
80 {$R *.lfm}
81 
CheckSearchPathnull82 function CheckSearchPath(const Context, ExpandedPath: string; Level: TCheckCompileOptionsMsgLvl; Hint: string = ''): boolean;
83 var
84   CurPath: string;
85   p: integer;
86   HasChars: TCCOSpecialChars;
87   ErrorMsg: string;
88 begin
89   Result := False;
90 
91   if Hint<>'' then Hint:=#13#13+Hint;
92 
93   // check for *
94   if Ord(Level) <= Ord(ccomlHints) then
95   begin
96     if System.Pos('*', ExpandedPath) > 0 then
97     begin
98       if IDEMessageDialog(lisHint, Format(
99         lisTheContainsAStarCharacterLazarusUsesThisAsNormalCh, [Context, LineEnding])+Hint,
100         mtWarning, [mbOK, mbCancel]) <> mrOk then
101         exit;
102     end;
103   end;
104 
105   // check for non existing directories
106   if Ord(Level) <= Ord(ccomlWarning) then
107   begin
108     p := 1;
109     repeat
110       //DebugLn(['CheckSearchPath ',ExpandedPath,' ',p,' ',length(ExpandedPath)]);
111       CurPath := GetNextDirectoryInSearchPath(ExpandedPath, p);
112       if (CurPath <> '') and (not IDEMacros.StrHasMacros(CurPath)) and
113         (FilenameIsAbsolute(CurPath)) then
114       begin
115         if not DirPathExistsCached(CurPath) then
116         begin
117           if IDEMessageDialog(lisCCOWarningCaption, Format(
118             lisTheContainsANotExistingDirectory, [Context, LineEnding, CurPath])+Hint,
119             mtWarning, [mbIgnore, mbCancel]) <> mrIgnore then
120             Exit;
121         end;
122       end;
123     until p > length(ExpandedPath);
124   end;
125 
126   // check for special characters
127   if (not IDEMacros.StrHasMacros(ExpandedPath)) then
128   begin
129     FindSpecialCharsInPath(ExpandedPath, HasChars);
130     if Ord(Level) <= Ord(ccomlWarning) then
131     begin
132       if Ord(Level) >= Ord(ccomlErrors) then
133         ErrorMsg := SpecialCharsToStr(HasChars * [ccoscSpecialChars, ccoscNewLine])
134       else
135         ErrorMsg := SpecialCharsToStr(HasChars);
136       if ErrorMsg <> '' then
137       begin
138         if IDEMessageDialog(lisCCOWarningCaption, Context + LineEnding + ErrorMsg+Hint,
139           mtWarning, [mbOK, mbCancel]) <> mrOk then
140           exit;
141       end;
142     end;
143   end;
144 
145   Result := True;
146 end;
147 
148 { TCompilerPathOptionsFrame }
149 
TCompilerPathOptionsFrame.Checknull150 function TCompilerPathOptionsFrame.Check: boolean;
151 var
152   NewParsedOutputDir: string;
153 
CheckPutSearchPathnull154   function CheckPutSearchPath(
155     const Context, OldExpandedPath, NewExpandedPath: string): boolean;
156   var
157     Level: TCheckCompileOptionsMsgLvl;
158     p: String;
159   begin
160     if OldExpandedPath <> NewExpandedPath then
161       Level := ccomlHints
162     else
163       Level := ccomlErrors;
164 
165     // do not complain about missing output directory
166     p:=NewExpandedPath;
167     if NewParsedOutputDir<>'' then
168       p:=RemoveSearchPaths(p,NewParsedOutputDir);
169 
170     Result := CheckSearchPath(Context, p, Level, lisHintClickOnShowOptionsToFindOutWhereInheritedPaths);
171   end;
172 
173 var
174   OldParsedIncludePath: String;
175   OldParsedLibraryPath: String;
176   OldParsedUnitPath: String;
177   OldParsedSrcPath: String;
178   OldParsedDebugPath: String;
179   OldUnparsedIncludePath: String;
180   OldUnparsedLibraryPath: String;
181   OldUnparsedUnitPath: String;
182   OldUnparsedSrcPath: String;
183   OldUnparsedDebugPath: String;
184   NewParsedIncludePath: String;
185   NewParsedLibraries: String;
186   NewParsedUnitPath: String;
187   NewParsedSrcPath: String;
188   NewParsedDebugPath: String;
189   PathsChanged: boolean;
190 
191   procedure GetParsedPaths;
192   begin
193     NewParsedOutputDir:=FCompilerOpts.GetUnitOutPath(False,coptParsed);
194     NewParsedIncludePath:=FCompilerOpts.GetIncludePath(False,coptParsed,false);
195     NewParsedLibraries:=FCompilerOpts.GetLibraryPath(False,coptParsed,false);
196     NewParsedUnitPath:=FCompilerOpts.GetUnitPath(False,coptParsed,false);
197     NewParsedSrcPath:=FCompilerOpts.GetSrcPath(False,coptParsed,false);
198     NewParsedDebugPath:=FCompilerOpts.GetDebugPath(False,coptParsed,false);
199   end;
200 
201 var
202   o: TParsedCompilerOptString;
203   Msg: String;
204 begin
205   Result:=false;
206   // Project compiler options have changed if BuildMode was changed by user.
207   if FHasProjectCompilerOpts then
208     FCompilerOpts := Project1.CompilerOptions;
209 
210   GetParsedPaths;
211   OldParsedIncludePath := NewParsedIncludePath;
212   OldUnparsedIncludePath := FCompilerOpts.IncludePath;
213   OldParsedLibraryPath := NewParsedLibraries;
214   OldUnparsedLibraryPath := FCompilerOpts.Libraries;
215   OldParsedUnitPath := NewParsedUnitPath;
216   OldUnparsedUnitPath := FCompilerOpts.OtherUnitFiles;
217   OldParsedSrcPath := NewParsedSrcPath;
218   OldUnparsedSrcPath := FCompilerOpts.SrcPath;
219   OldParsedDebugPath := NewParsedDebugPath;
220   OldUnparsedDebugPath := FCompilerOpts.DebugPath;
221 
222   try
223     FCompilerOpts.IncludePath := IncludeFilesEdit.Text;
224     FCompilerOpts.Libraries := LibrariesEdit.Text;
225     FCompilerOpts.OtherUnitFiles := OtherUnitsEdit.Text;
226     FCompilerOpts.SrcPath := OtherSourcesEdit.Text;
227     FCompilerOpts.DebugPath := DebugPathEdit.Text;
228     GetParsedPaths;
229 
230     if FCompilerOpts.ParsedOpts.HasParsedError then begin
231       o:=FCompilerOpts.ParsedOpts.ParsedErrorOption;
232       case o of
233       pcosBaseDir:
234         Msg:=lisIWonderHowYouDidThatErrorInTheBaseDirectory;
235       pcosUnitPath:
236         Msg:=lisErrorInTheSearchPathForOtherUnitFiles;
237       pcosIncludePath:
238         Msg:=lisErrorInTheSearchPathForIncludeFiles;
239       pcosObjectPath:
240         Msg:=lisErrorInTheSearchPathForObjectFiles;
241       pcosLibraryPath:
242         Msg:=lisErrorInTheSearchPathForLibraries;
243       pcosSrcPath:
244         Msg:=lisErrorInTheSearchPathForOtherSources;
245       pcosLinkerOptions:
246         Msg:=lisErrorInTheCustomLinkerOptionsLinkingPassOptionsToL;
247       pcosCustomOptions:
248         Msg:=lisErrorInTheCustomCompilerOptionsOther;
249       pcosOutputDir:
250         Msg:=lisErrorInTheUnitOutputDirectory;
251       pcosCompilerPath:
252         Msg:=lisErrorInTheCompilerFileName;
253       pcosDebugPath:
254         Msg:=lisErrorInTheDebuggerPathAddition;
255       else
256         Msg:=Format(lisIWonderHowYouDidThatErrorInThe, [EnumToStr(o)]);
257       end;
258       Msg:=Msg+LineEnding+FCompilerOpts.ParsedOpts.ParsedErrorMsg+LineEnding
259         +lisValue3+dbgstr(FCompilerOpts.ParsedOpts.Values[o].UnparsedValue);
260       IDEMessageDialog(lisCCOErrorCaption, Msg, mtError, [mbCancel]);
261       exit;
262     end;
263 
264     if not CheckPutSearchPath('include search path', OldParsedIncludePath, NewParsedIncludePath) then
265       Exit;
266     if not CheckPutSearchPath('library search path', OldParsedLibraryPath, NewParsedLibraries) then
267       Exit;
268     if not CheckPutSearchPath('unit search path', OldParsedUnitPath, NewParsedUnitPath) then
269       Exit;
270     if not CheckPutSearchPath('source search path', OldParsedSrcPath, NewParsedSrcPath) then
271       Exit;
272     if not CheckPutSearchPath('debugger search path', OldParsedDebugPath, NewParsedDebugPath) then
273       Exit;
274 
275     if not CheckSrcPathInUnitPath(OldParsedSrcPath,NewParsedSrcPath,
276       OldParsedUnitPath,NewParsedUnitPath,PathsChanged)
277     then
278       Exit;
279     if PathsChanged then
280       GetParsedPaths;
281 
282   finally
283     FCompilerOpts.IncludePath := OldUnparsedIncludePath;
284     FCompilerOpts.Libraries := OldUnparsedLibraryPath;
285     FCompilerOpts.OtherUnitFiles := OldUnparsedUnitPath;
286     FCompilerOpts.SrcPath := OldUnparsedSrcPath;
287     FCompilerOpts.DebugPath := OldUnparsedDebugPath;
288   end;
289   Result := True;
290 end;
291 
TCompilerPathOptionsFrame.GetTitlenull292 function TCompilerPathOptionsFrame.GetTitle: string;
293 begin
294   Result := dlgSearchPaths;
295 end;
296 
297 procedure TCompilerPathOptionsFrame.DoShowOptions(Sender: TObject);
298 begin
299   DoSaveSettings(FCompilerOpts);
300   ShowCompilerOptionsDialog(FDialog, FCompilerOpts);
301 end;
302 
303 procedure TCompilerPathOptionsFrame.DoCheck(Sender: TObject);
304 begin
305   DoSaveSettings(FCompilerOpts);
306   if Assigned(TestCompilerOptions) then
307   begin
308     btnCheck.Enabled := False;
309     try
310       TestCompilerOptions(FCompilerOpts);
311     finally
312       btnCheck.Enabled := True;
313     end;
314   end;
315 end;
316 
317 procedure TCompilerPathOptionsFrame.DoImport(Sender: TObject);
318 begin
319   DoSaveSettings(FCompilerOpts);
320   if (ShowImportCompilerOptionsDialog(FCompilerOpts, FDialog) = mrOK)
321   and Assigned(OnLoadIDEOptions) then
322     OnLoadIDEOptions(Self, FCompilerOpts);
323 end;
324 
325 procedure TCompilerPathOptionsFrame.DoExport(Sender: TObject);
326 begin
327   DoSaveSettings(FCompilerOpts);
328   if (ShowExportCompilerOptionsDialog(FCompilerOpts, FDialog) = mrOK)
329   and Assigned(OnSaveIDEOptions) then
330     OnSaveIDEOptions(Self, FCompilerOpts);
331 end;
332 
333 procedure TCompilerPathOptionsFrame.DoSaveSettings(AOptions: TAbstractIDEOptions);
334 begin
335   if Assigned(OnSaveIDEOptions) then
336     OnSaveIDEOptions(Self, AOptions);
337 end;
338 
339 procedure TCompilerPathOptionsFrame.UpdateTargetFileLabel;
340 begin
341   if ProjTargetFileEdit.Text<>'' then
342     ProjTargetFileLabel.Caption:=lisTargetFileNameO
343   else
344     ProjTargetFileLabel.Caption:=lisTargetFileNameEmptyUseUnitOutputDirectory;
345 end;
346 
347 constructor TCompilerPathOptionsFrame.Create(TheOwner: TComponent);
348 begin
349   inherited Create(TheOwner);
350   FCompilerOpts := nil;
351 end;
352 
353 procedure TCompilerPathOptionsFrame.ProjTargetFileEditChange(Sender: TObject);
354 begin
355   UpdateTargetFileLabel;
356 end;
357 
TCompilerPathOptionsFrame.CheckSrcPathInUnitPathnull358 function TCompilerPathOptionsFrame.CheckSrcPathInUnitPath(OldParsedSrcPath,
359   NewParsedSrcPath, OldParsedUnitPath, NewParsedUnitPath: string; out
360   SrcPathChanged: boolean): boolean;
361 // checks if the SrcPath contains directories of the UnitPath
362 // the SrcPath should only contain directories for the IDE, not for the compiler
363 var
364   p: Integer;
365   CurPath: String;
366   Duplicates: TStringList;
367   i: PtrUInt;
368   OldUnparsedSrcPath: String;
369   NewUnparsedSrcPath: String;
370   j: Integer;
371   BaseDir: String;
372 begin
373   Result:=true;
374   SrcPathChanged:=false;
375   if (OldParsedSrcPath=NewParsedSrcPath)
376   and (OldParsedUnitPath=NewParsedUnitPath) then exit;
377 
378   Duplicates:=TStringList.Create;
379   try
380     p:=1;
381     i:=0;
382     BaseDir:=AppendPathDelim(FCompilerOpts.BaseDirectory);
383     repeat
384       CurPath:=GetNextDirectoryInSearchPath(NewParsedSrcPath, p);
385       if (CurPath<>'') and (not IDEMacros.StrHasMacros(CurPath)) and
386         (FilenameIsAbsolute(CurPath)) then
387       begin
388         if (SearchDirectoryInSearchPath(NewParsedUnitPath,CurPath)>0)
389         or (CompareFilenames(BaseDir,AppendPathDelim(CurPath))=0) then
390           Duplicates.AddObject(CurPath,TObject({%H-}Pointer(i)));
391       end;
392       inc(i);
393     until p>length(NewParsedSrcPath);
394 
395     if Duplicates.Count>0 then
396     begin
397       debugln(['TCompilerPathOptionsFrame.CheckSrcPathInUnitPath OldParsedSrcPath="',OldParsedSrcPath,'" NewParsedSrcPath="',NewParsedSrcPath,'" OldParsedUnitPath="',OldParsedUnitPath,'" NewParsedUnitPath="',NewParsedUnitPath,'"']);
398       Result:=false;
399       Duplicates.Delimiter:=#13;
400       Duplicates.StrictDelimiter:=true;
401       if IDEQuestionDialog(lisDuplicateSearchPath,
402         Format(lisTheOtherSourcesContainsADirectoryWhichIsAlreadyInT,
403               [LineEnding+LineEnding, Duplicates.DelimitedText]),
404         mtError, [mrCancel,
405                   mrYes, lisRemoveThePathsFromOtherSources, 'IsDefault']) = mrYes
406       then begin
407         // remove paths from SrcPath
408         OldUnparsedSrcPath:=FCompilerOpts.SrcPath;
409         NewUnparsedSrcPath:='';
410         i:=0;
411         p:=1;
412         repeat
413           CurPath:=GetNextDirectoryInSearchPath(OldUnparsedSrcPath, p);
414           j:=Duplicates.Count-1;
415           while (j>=0) and (PtrUInt(Duplicates.Objects[j])<>i) do dec(j);
416           if j<0 then
417           begin
418             if NewUnparsedSrcPath<>'' then
419               NewUnparsedSrcPath:=NewUnparsedSrcPath+';';
420             NewUnparsedSrcPath:=NewUnparsedSrcPath+CurPath;
421           end;
422           inc(i);
423         until p>length(OldUnparsedSrcPath);
424         FCompilerOpts.SrcPath:=NewUnparsedSrcPath;
425         OtherSourcesEdit.Text:=FCompilerOpts.SrcPath;
426 
427         SrcPathChanged:=true;
428         // do not set Result to true, let's user review the changes
429       end;
430     end;
431   finally
432     Duplicates.Free;
433   end;
434 end;
435 
436 procedure TCompilerPathOptionsFrame.PathEditBtnClick(Sender: TObject);
437 begin
438   if Sender is TPathEditorButton then
439     TPathEditorButton(Sender).CurrentPathEditor.BaseDirectory := FCompilerOpts.BaseDirectory;
440 end;
441 
PathEditBtnExecutednull442 function TCompilerPathOptionsFrame.PathEditBtnExecuted(Context: String; var NewPath: String): Boolean;
443 var
444   ExpandedPath: string;
445 begin
446   NewPath := FCompilerOpts.ShortenPath(NewPath);
447   ExpandedPath := TrimSearchPath(NewPath, FCompilerOpts.BaseDirectory, true);
448   Result := CheckSearchPath(Context, ExpandedPath, ccomlHints);
449 end;
450 
451 procedure TCompilerPathOptionsFrame.FileBrowseBtnClick(Sender: TObject);
452 var
453   OpenDialog: TOpenDialog;
454   DefaultFilename: string;
455   NewFilename: string;
456 begin
457   OpenDialog := TSelectDirectoryDialog.Create(Self);
458   try
459     DefaultFilename := '';
460     if Sender = btnUnitOutputDir then
461     begin
462       OpenDialog.Title := lisUnitOutputDirectory;
463       OpenDialog.Options := OpenDialog.Options + [ofPathMustExist];
464     end
465     else
466       Exit;
467     OpenDialog.Filename := ExtractFilename(DefaultFilename);
468     if DefaultFilename <> '' then
469       OpenDialog.InitialDir := ExtractFilePath(DefaultFilename)
470     else
471       OpenDialog.InitialDir := FCompilerOpts.BaseDirectory;
472     if OpenDialog.Execute then
473     begin
474       NewFilename := TrimFilename(OpenDialog.Filename);
475       NewFilename := FCompilerOpts.ShortenPath(NewFilename);
476       if Sender = btnUnitOutputDir then
477         UnitOutputDirEdit.Text := OpenDialog.Filename;
478     end;
479   finally
480     OpenDialog.Free;
481   end;
482 end;
483 
484 procedure TCompilerPathOptionsFrame.Setup(ADialog: TAbstractOptionsEditorDialog);
485 
486   function CreateButton(ACaption: String; AKind: TBitBtnKind = bkCustom): TBitBtn;
487   begin
488     Result := ADialog.AddButton;
489     Result.Kind := AKind;
490     Result.Caption := ACaption;
491   end;
492 
493 begin
494   FDialog:=ADialog;
495   ProjTargetFileEdit.Text:='';
496   ProjTargetApplyConventionsCheckBox.Caption:=lisApplyConventions;
497   ProjTargetApplyConventionsCheckBox.Hint:=lisApplyConventionsHint;
498 
499   OtherUnitsLabel.Caption := dlgOtherUnitFiles;
500   OtherUnitsPathEditBtn := TPathEditorButton.Create(Self);
501   with OtherUnitsPathEditBtn do
502   begin
503     Name := 'OtherUnitsPathEditBtn';
504     Caption := '...';
505     Parent := Self;
506     TabOrder := 1;
507     Anchors := [akRight, akTop, akBottom];
508     AnchorParallel(akTop, 0, OtherUnitsEdit);
509     AnchorParallel(akBottom, 0, OtherUnitsEdit);
510     AnchorParallel(akRight, 0, Self);
511     Width := Height;
512     AssociatedEdit := OtherUnitsEdit;
513     ContextCaption := OtherUnitsLabel.Caption;
514     Templates:='$(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS)' +
515               ';$(LazarusDir)/lcl/units/$(TargetCPU)-$(TargetOS)/$(LCLWidgetType)' +
516               ';$(LazarusDir)/components/codetools/units/$(TargetCPU)-$(TargetOS)' +
517               ';$(LazarusDir)/components/custom' +
518               ';$(LazarusDir)/packager/units/$(TargetCPU)-$(TargetOS)';
519     OnClick := @PathEditBtnClick;
520     OnExecuted := @PathEditBtnExecuted;
521   end;
522   OtherUnitsEdit.AnchorToNeighbour(akRight, 0, OtherUnitsPathEditBtn);
523 
524   {------------------------------------------------------------}
525 
526   IncludeFilesLabel.Caption := dlgCOIncFiles;
527   IncludeFilesPathEditBtn := TPathEditorButton.Create(Self);
528   with IncludeFilesPathEditBtn do
529   begin
530     Name := 'IncludeFilesPathEditBtn';
531     Caption := '...';
532     Parent := Self;
533     TabOrder := 3;
534     Anchors := [akRight, akTop, akBottom];
535     AnchorParallel(akTop, 0, IncludeFilesEdit);
536     AnchorParallel(akBottom, 0, IncludeFilesEdit);
537     AnchorParallel(akRight, 0, Self);
538     Width := Height;
539     AssociatedEdit := IncludeFilesEdit;
540     ContextCaption := IncludeFilesLabel.Caption;
541     Templates := 'include;inc';
542     OnClick := @PathEditBtnClick;
543     OnExecuted := @PathEditBtnExecuted;
544   end;
545   IncludeFilesEdit.AnchorToNeighbour(akRight, 0, IncludeFilesPathEditBtn);
546 
547   {------------------------------------------------------------}
548 
549   OtherSourcesLabel.Caption := dlgCOSources;
550   OtherSourcesPathEditBtn := TPathEditorButton.Create(Self);
551   with OtherSourcesPathEditBtn do
552   begin
553     Name := 'OtherSourcesPathEditBtn';
554     Caption := '...';
555     Parent := Self;
556     TabOrder := 9;
557     Anchors := [akRight, akTop, akBottom];
558     AnchorParallel(akTop, 0, OtherSourcesEdit);
559     AnchorParallel(akBottom, 0, OtherSourcesEdit);
560     AnchorParallel(akRight, 0, Self);
561     Width := Height;
562     AssociatedEdit := OtherSourcesEdit;
563     ContextCaption := OtherSourcesLabel.Caption;
564     Templates := '$(LazarusDir)/lcl' +
565                 ';$(LazarusDir)/lcl/interfaces/$(LCLWidgetType)' +
566                 ';$(LazarusDir)/components/synedit' +
567                 ';$(LazarusDir)/components/codetools';
568     OnClick := @PathEditBtnClick;
569     OnExecuted := @PathEditBtnExecuted;
570   end;
571   OtherSourcesEdit.AnchorToNeighbour(akRight, 0, OtherSourcesPathEditBtn);
572 
573   {------------------------------------------------------------}
574 
575   LibrariesLabel.Caption := dlgCOLibraries;
576   LibrariesPathEditBtn := TPathEditorButton.Create(Self);
577   with LibrariesPathEditBtn do
578   begin
579     Name := 'LibrariesPathEditBtn';
580     Caption := '...';
581     Parent := Self;
582     TabOrder := 5;
583     Anchors := [akRight, akTop, akBottom];
584     AnchorParallel(akTop, 0, LibrariesEdit);
585     AnchorParallel(akBottom, 0, LibrariesEdit);
586     AnchorParallel(akRight, 0, Self);
587     Width := Height;
588     AssociatedEdit := LibrariesEdit;
589     ContextCaption := LibrariesLabel.Caption;
590     Templates := '/usr/X11R6/lib;/sw/lib';
591     OnClick := @PathEditBtnClick;
592     OnExecuted := @PathEditBtnExecuted;
593   end;
594   LibrariesEdit.AnchorToNeighbour(akRight, 0, LibrariesPathEditBtn);
595 
596   {------------------------------------------------------------}
597 
598   UnitOutputDirLabel.Caption := dlgUnitOutp;
599   btnUnitOutputDir := TButton.Create(Self);
600   with btnUnitOutputDir do
601   begin
602     Name := 'btnUnitOutputDir';
603     Caption := '...';
604     Parent := Self;
605     TabOrder := 7;
606     Anchors := [akRight, akTop, akBottom];
607     AnchorParallel(akTop, 0, UnitOutputDirEdit);
608     AnchorParallel(akBottom, 0, UnitOutputDirEdit);
609     AnchorParallel(akRight, 0, Self);
610     Width := Height;
611     OnClick := @FileBrowseBtnClick;
612   end;
613   UnitOutputDirEdit.AnchorToNeighbour(akRight, 0, btnUnitOutputDir);
614 
615   {------------------------------------------------------------}
616 
617   DebugPathLabel.Caption := dlgCODebugPath;
618   DebugPathEditBtn := TPathEditorButton.Create(Self);
619   with DebugPathEditBtn do
620   begin
621     Name := 'DebugPathEditBtn';
622     Caption := '...';
623     Parent := Self;
624     TabOrder := 13;
625     Anchors := [akRight, akTop, akBottom];
626     AnchorParallel(akTop, 0, DebugPathEdit);
627     AnchorParallel(akBottom, 0, DebugPathEdit);
628     AnchorParallel(akRight, 0, Self);
629     Width := Height;
630     AssociatedEdit := DebugPathEdit;
631     ContextCaption := DebugPathLabel.Caption;
632     Templates := '$(LazarusDir)/lcl/include' +
633                 ';$(LazarusDir)/lcl/interfaces/$(LCLWidgetType)' +
634                 ';$(LazarusDir)/include';
635     OnClick := @PathEditBtnClick;
636     OnExecuted := @PathEditBtnExecuted;
637   end;
638   DebugPathEdit.AnchorToNeighbour(akRight, 0, DebugPathEditBtn);
639 
640   {------------------------------------------------------------}
641 
642   // register special buttons in the dialog itself
643   btnShowOptions := CreateButton(dlgCOShowOptions);
644   IDEImages.AssignImage(btnShowOptions, 'menu_compiler_options');
645   btnShowOptions.OnClick := @DoShowOptions;
646   // Check
647   btnCheck := CreateButton(lisCompTest);
648   btnCheck.ModalResult := mrNone;
649   btnCheck.OnClick  := @DoCheck;
650   btnCheck.LoadGlyphFromStock(idButtonYes);
651 
652   ADialog.AddButtonSeparator;
653 
654   // Export
655   btnExport := CreateButton(lisExport);
656   btnExport.OnClick := @DoExport;
657   btnExport.Hint := dlgCOLoadSaveHint;
658   btnExport.LoadGlyphFromStock(idButtonSave);
659   // Import
660   btnLoadSave := CreateButton(lisImport);
661   btnLoadSave.OnClick := @DoImport;
662   btnLoadSave.Hint := dlgCOLoadSaveHint;
663   btnLoadSave.LoadGlyphFromStock(idButtonOpen);
664   if btnLoadSave.Glyph.Empty then
665     IDEImages.AssignImage(btnLoadSave, 'laz_save');
666 
667   ADialog.AddButtonSeparator;
668 
669   chkUseAsDefault := TCheckBox(ADialog.AddControl(TCheckBox));
670   chkUseAsDefault.Caption := dlgCOSetAsDefault;
671   chkUseAsDefault.ShowHint := True;
672   chkUseAsDefault.Hint := lisWhenEnabledTheCurrentOptionsAreSavedToTheTemplateW;
673 end;
674 
675 procedure TCompilerPathOptionsFrame.ReadSettings(AOptions: TAbstractIDEOptions);
676 var
677   ProjOpts: TProjectCompilerOptions;
678 begin
679   if not (AOptions is TBaseCompilerOptions) then exit;
680   FCompilerOpts := TBaseCompilerOptions(AOptions);
681 
682   if AOptions is TProjectCompilerOptions then
683   begin
684     ProjOpts:=TProjectCompilerOptions(AOptions);
685     FHasProjectCompilerOpts:=True;
686     ProjTargetFileEdit.Visible:=true;
687     ProjTargetFileLabel.Visible:=true;
688     ProjTargetFileEdit.Text:=ProjOpts.TargetFilename;
689     ProjTargetApplyConventionsCheckBox.Checked:=ProjOpts.TargetFilenameApplyConventions;
690     ProjTargetApplyConventionsCheckBox.Visible:=true;
691     UpdateTargetFileLabel;
692   end else begin
693     FHasProjectCompilerOpts:=False;
694     ProjTargetFileEdit.Visible:=false;
695     ProjTargetFileLabel.Visible:=false;
696     ProjTargetApplyConventionsCheckBox.Visible:=false;
697   end;
698 
699   SetPathTextAndHint(FCompilerOpts.OtherUnitFiles, OtherUnitsEdit);
700   SetPathTextAndHint(FCompilerOpts.IncludePath, IncludeFilesEdit);
701   SetPathTextAndHint(FCompilerOpts.Libraries, LibrariesEdit);
702   SetPathTextAndHint(FCompilerOpts.SrcPath, OtherSourcesEdit);
703   UnitOutputDirEdit.Text := FCompilerOpts.UnitOutputDirectory;
704   SetPathTextAndHint(FCompilerOpts.DebugPath, DebugPathEdit);
705 
706   chkUseAsDefault.Visible := FCompilerOpts.CanBeDefaulForProject;
707 end;
708 
709 procedure TCompilerPathOptionsFrame.WriteSettings(AOptions: TAbstractIDEOptions);
710 var
711   ProjCompOpts: TProjectCompilerOptions;
712 begin
713   if AOptions is TProjectCompilerOptions then begin
714     ProjCompOpts:=TProjectCompilerOptions(AOptions);
715     ProjCompOpts.TargetFilename:=ProjTargetFileEdit.Text;
716     ProjCompOpts.TargetFilenameApplyConventions:=ProjTargetApplyConventionsCheckBox.Checked;
717     ProjCompOpts.LazProject.UseAsDefault := chkUseAsDefault.Checked;
718   end;
719 
720   with AOptions as TBaseCompilerOptions do
721   begin
722     OtherUnitFiles := OtherUnitsEdit.Text;
723     IncludePath := IncludeFilesEdit.Text;
724     Libraries := LibrariesEdit.Text;
725     SrcPath := OtherSourcesEdit.Text;
726     UnitOutputDirectory := UnitOutputDirEdit.Text;
727     DebugPath := DebugPathEdit.Text;
728   end;
729 end;
730 
731 class function TCompilerPathOptionsFrame.SupportedOptionsClass: TAbstractIDEOptionsClass;
732 begin
733   Result := TBaseCompilerOptions;
734 end;
735 
736 initialization
737   RegisterIDEOptionsEditor(GroupCompiler, TCompilerPathOptionsFrame,
738     CompilerOptionsSearchPaths);
739   RegisterIDEOptionsEditor(GroupPkgCompiler, TCompilerPathOptionsFrame,
740     CompilerOptionsSearchPaths);
741 
742 end.
743 
744