1 { Extern help options frame for Lazarus IDE.
2 
3   Copyright (C) 2010  Mattias Gaertner  mattias@freepascal.org
4 
5   This library is free software; you can redistribute it and/or modify it
6   under the terms of the GNU Library General Public License as published by
7   the Free Software Foundation; either version 2 of the License, or (at your
8   option) any later version with the following modification:
9 
10   As a special exception, the copyright holders of this library give you
11   permission to link this library with independent modules to produce an
12   executable, regardless of the license terms of these independent modules,and
13   to copy and distribute the resulting executable under terms of your choice,
14   provided that you also meet, for each linked independent module, the terms
15   and conditions of the license of that module. An independent module is a
16   module which is not derived from or based on this library. If you modify
17   this library, you may extend this exception to your version of the library,
18   but you are not obligated to do so. If you do not wish to do so, delete this
19   exception statement from your version.
20 
21   This program is distributed in the hope that it will be useful, but WITHOUT
22   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
23   FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
24   for more details.
25 
26   You should have received a copy of the GNU Library General Public License
27   along with this library; if not, write to the Free Software Foundation,
28   Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA.
29 }
30 
31 unit ExternHelpFrm;
32 
33 {$mode objfpc}{$H+}
34 
35 interface
36 
37 uses
38   Classes, SysUtils,
39   // LazUtils
40   FileUtil, LazFileUtils, LazConfigStorage,
41   // LCL
42   LCLProc, LResources, Forms, Controls, Graphics, Dialogs, ComCtrls, Buttons, StdCtrls,
43   ExtCtrls, ButtonPanel, HelpIntfs,
44   // IdeIntf
45   LazHelpIntf, PackageIntf, MacroIntf, IDEOptionsIntf, IDEOptEditorIntf,
46   LazIDEIntf, BaseIDEIntf, IDEDialogs, IDEImagesIntf, SrcEditorIntf;
47 
48 const
49   ExternHelpConfigVersion = 1;
50 var
51   ExternHelpOptionID: integer = 2000;
52   ExternHelpOptionGeneralID: integer = 1000;
53 
54 resourcestring
55   ehrsGroupTitle = 'Extern help';
56   ehrsName = 'Name';
57   ehrsUnitFileOrUnitDirectory = 'Unit file or unit directory';
58   ehrsURL = 'URL';
59   ehrsHelp = 'Help';
60   ehrsAddNewItem = 'Add new item';
61   ehrsDeleteItem = 'Delete item';
62   ehrsBrowseForPath = 'Browse for path';
63   ehrsGeneral = 'General';
64   ehrsBrowse = 'Browse ...';
65   ehrsMacrofy = 'Use macros';
66   ehrsReplaceCommonDirectoriesWithMacros = 'Replace common directories with '
67     +'macros';
68   ehrsEditorFile = 'Editor file ...';
69   ehrsStoreThisURLIn = 'Store this URL in';
70   ehrsIncludeSubDirectories = 'Include sub directories';
71   ehrsSelectAFileFromTheSourceEditor = 'Select a file from the source editor';
72   ehrsChooseAPascalUnit = 'Choose a pascal unit';
73   ehrsSelectFile = 'Select file';
74   ehrsDirectoryNotFound = 'Directory not found: %s';
75   ehrsFileNotFound = 'File not found: %s';
76   ehrsWarning = 'Warning';
77   ehrsExternal = 'External';
78   ehrsMySettings = 'My settings (default)';
79 
80 type
81 
82   { TExternHelpFileSelector }
83 
84   TExternHelpFileSelector = class(TForm)
85   published
86     FileListBox: TListBox;
87     ButtonPanel1: TButtonPanel;
88     procedure ButtonPanel1OKButtonClick(Sender: TObject);
89   public
90     constructor Create(TheOwner: TComponent); override;
91   end;
92 
93   { TExternHelpItem }
94 
95   TExternHelpItem = class
96   private
97     FChangeStep: integer;
98     fChilds: TFPList;
99     FFilename: string;
100     FName: string;
101     FStoreIn: string;
102     FURL: string;
103     FWithSubDirectories: boolean;
GetChildCountnull104     function GetChildCount: integer;
GetChildrennull105     function GetChildren(Index: integer): TExternHelpItem;
106     procedure SetFilename(const AValue: string);
107     procedure SetName(const AValue: string);
108     procedure SetStoreIn(const AValue: string);
109     procedure SetURL(const AValue: string);
110     procedure SetWithSubDirectories(const AValue: boolean);
111   public
112     Parent: TExternHelpItem;
113     constructor Create;
114     destructor Destroy; override;
115     procedure Clear;
116     procedure AddChild(Item: TExternHelpItem);
117     procedure MoveChild(FromPos, ToPos: integer);
118     procedure RemoveChild(Index: integer);
119     procedure DeleteChild(Index: integer);
120     procedure DeleteChild(Child: TExternHelpItem);
IndexOfnull121     function IndexOf(Child: TExternHelpItem): integer;
IsEqualnull122     function IsEqual(Item: TExternHelpItem; WithName: boolean): boolean;
IsDirectorynull123     function IsDirectory: boolean;
124     procedure Assign(Src: TExternHelpItem; WithName: boolean);
125     procedure IncreaseChangeStep; virtual;
126     property Name: string read FName write SetName;
127     property Filename: string read FFilename write SetFilename;
128     property WithSubDirectories: boolean read FWithSubDirectories write SetWithSubDirectories;
129     property URL: string read FURL write SetURL;
130     property StoreIn: string read FStoreIn write SetStoreIn;
131     property ChildCount: integer read GetChildCount;
132     property Children[Index: integer]: TExternHelpItem read GetChildren;
133     property ChangeStep: integer read FChangeStep;
134   end;
135 
136   TExternHelpOptions = class;
137 
138   { TExternHelpRootItem }
139 
140   TExternHelpRootItem = class(TExternHelpItem)
141   public
142     Owner: TExternHelpOptions;
143     procedure IncreaseChangeStep; override;
144   end;
145 
146   { TExternalHelpDatabase }
147 
148   TExternalHelpDatabase = class(THelpDatabase)
149   public
150     constructor Create(TheOwner: TComponent); override;
151     destructor Destroy; override;
ShowHelpnull152     function ShowHelp(Query: THelpQuery; BaseNode, NewNode: THelpNode;
153                       QueryItem: THelpQueryItem;
154                       var ErrMsg: string): TShowHelpResult; override;
155   end;
156 
157   { TExternHelpOptions }
158 
159   TExternHelpOptions = class(TAbstractIDEEnvironmentOptions)
160   private
161     FChangeStep: integer;
162     FFilename: string;
163     FHelpDB: TExternalHelpDatabase;
164     FLastSavedChangeStep: integer;
165     procedure SetFilename(const AValue: string);
166     procedure PkgFileLoaded(Sender: TObject);
167     procedure LoadNode(Config: TConfigStorage; Path: string; Node: TExternHelpItem);
168     procedure SaveNode(Config: TConfigStorage; Path: string; Node: TExternHelpItem);
169   public
170     RootItem: TExternHelpRootItem;
171     constructor Create;
172     destructor Destroy; override;
173     procedure Clear;
174     procedure ClearItemsStoredInUserSettings;
175     procedure ClearItemsStoredInPackages(Parent: TExternHelpItem;
176                                          const Name: string = '*');
GetGroupCaptionnull177     class function GetGroupCaption: string; override;
GetInstancenull178     class function GetInstance: TAbstractIDEOptions; override;
Loadnull179     function Load(Config: TConfigStorage; KeepPackageOpts: boolean): TModalResult; virtual;
Savenull180     function Save(Config: TConfigStorage): TModalResult; virtual;
181     procedure LoadOptionsFromPackage(Pkg: TIDEPackage; Parent: TExternHelpItem);
182     procedure LoadOptionsFromPackages;
183     procedure SaveOptionsToPackage(Pkg: TIDEPackage);
184     procedure SaveOptionsToPackages;
LoadFromFilenull185     function LoadFromFile(Filename: string; KeepPackageOpts: boolean): TModalResult; virtual;
SaveToFilenull186     function SaveToFile(Filename: string): TModalResult; virtual;
Loadnull187     function Load(KeepPackageOpts: boolean): TModalResult; virtual;
Savenull188     function Save: TModalResult; virtual;
GetFullFilenamenull189     function GetFullFilename: string;
IsEqualnull190     function IsEqual(Src: TExternHelpOptions): boolean;
191     procedure Assign(Src: TExternHelpOptions); reintroduce;
192     procedure IncreaseChangeStep;
193     procedure UpdateHelpDB;
194     property Filename: string read FFilename write SetFilename;
195     property ChangeStep: integer read FChangeStep;
196     property LastSavedChangeStep: integer read FLastSavedChangeStep;
197     property HelpDB: TExternalHelpDatabase read FHelpDB;
198   end;
199 
200 type
201 
202   { TExternHelpGeneralOptsFrame }
203 
204   TExternHelpGeneralOptsFrame = class(TAbstractIDEOptionsEditor)
205     AddSpeedButton: TSpeedButton;
206     SelEditorFileButton: TButton;
207     WithSubDirsCheckBox: TCheckBox;
208     FileUseMacrosButton: TButton;
209     DeleteSpeedButton: TSpeedButton;
210     FileBrowseButton: TButton;
211     FilenameEdit: TEdit;
212     FilenameLabel: TLabel;
213     HelpBitBtn: TBitBtn;
214     ItemsTreeView: TTreeView;
215     NameEdit: TEdit;
216     NameLabel: TLabel;
217     Splitter1: TSplitter;
218     StoreComboBox: TComboBox;
219     StoreLabel: TLabel;
220     URLLabel: TLabel;
221     URLMemo: TMemo;
222     procedure AddSpeedButtonClick(Sender: TObject);
223     procedure DeleteSpeedButtonClick(Sender: TObject);
224     procedure FileBrowseButtonClick(Sender: TObject);
225     procedure FileUseMacrosButtonClick(Sender: TObject);
226     procedure FilenameEditChange(Sender: TObject);
227     procedure FilenameEditEditingDone(Sender: TObject);
228     procedure ItemsTreeViewDragOver(Sender, Source: TObject; X, Y: Integer;
229       State: TDragState; var Accept: Boolean);
230     procedure ItemsTreeViewEdited(Sender: TObject; Node: TTreeNode;
231       var S: string);
232     procedure ItemsTreeViewEditing(Sender: TObject; Node: TTreeNode;
233       var AllowEdit: Boolean);
234     procedure ItemsTreeViewEndDrag(Sender, Target: TObject; X, Y: Integer);
235     procedure ItemsTreeViewMouseMove(Sender: TObject; Shift: TShiftState; X,
236       Y: Integer);
237     procedure ItemsTreeViewSelectionChanged(Sender: TObject);
238     procedure ItemsTreeViewStartDrag(Sender: TObject;
239       var DragObject: TDragObject);
240     procedure NameEditChange(Sender: TObject);
241     procedure NameEditEditingDone(Sender: TObject);
242     procedure SelEditorFileButtonClick(Sender: TObject);
243     procedure StoreComboBoxEditingDone(Sender: TObject);
244     procedure URLMemoEditingDone(Sender: TObject);
245     procedure WithSubDirsCheckBoxEditingDone(Sender: TObject);
246   private
247     FOptions: TExternHelpOptions;
248     FDragNode: TTreeNode;
249     FMySettingsCaption: string;
250     procedure FillItemsTreeView;
251     procedure NameChanged(TVNode: TTreeNode; var NewName: string;
252       UpdateTree, UpdateEdit: boolean);
253     procedure StoreInChanged(TVNode: TTreeNode; var NewStoreIn: string;
254       UpdateTree, UpdateEdit: boolean);
255     procedure SelectionChanged;
FindTVNodenull256     function FindTVNode(NodeText: string): TTreeNode;
CreateUniqueNamenull257     function CreateUniqueName(Prefix: string): string;
258     procedure FillStoreInCombobox;
Macrofynull259     function Macrofy(Filename: string): string;
260   public
261     constructor Create(TheOwner: TComponent); override;
262     destructor Destroy; override;
GetTitlenull263     function GetTitle: String; override;
264     procedure ReadSettings(AOptions: TAbstractIDEOptions); override;
265     procedure Setup(ADialog: TAbstractOptionsEditorDialog); override;
SupportedOptionsClassnull266     class function SupportedOptionsClass: TAbstractIDEOptionsClass; override;
267     procedure WriteSettings(AOptions: TAbstractIDEOptions); override;
268     property Options: TExternHelpOptions read FOptions;
269   end;
270 
271 var
272   ExternHelpOptions: TExternHelpOptions = nil;
273 
274 procedure Register;
275 
276 implementation
277 
278 {$R *.lfm}
279 
280 procedure Register;
281 begin
282   ExternHelpOptions:=TExternHelpOptions.Create;
283   ExternHelpOptionID:=GroupHelp;
284   ExternHelpOptionGeneralID:=RegisterIDEOptionsEditor(ExternHelpOptionID,
285       TExternHelpGeneralOptsFrame,ExternHelpOptionGeneralID)^.Index;
286   try
287     ExternHelpOptions.Load(false);
288     ExternHelpOptions.LoadOptionsFromPackages;
289   except
290     on E: Exception do begin
291       DebugLn(['Error reading externhelp options ',ExternHelpOptions.Filename,': ',E.Message]);
292     end;
293   end;
294   ExternHelpOptions.UpdateHelpDB;
295 end;
296 
297 { TExternHelpOptions }
298 
299 procedure TExternHelpOptions.SetFilename(const AValue: string);
300 begin
301   if FFilename=AValue then exit;
302   FFilename:=AValue;
303 end;
304 
305 procedure TExternHelpOptions.PkgFileLoaded(Sender: TObject);
306 begin
307   if Sender is TIDEPackage then
308     LoadOptionsFromPackage(TIDEPackage(Sender),RootItem);
309 end;
310 
311 procedure TExternHelpOptions.LoadNode(Config: TConfigStorage; Path: string;
312   Node: TExternHelpItem);
313 var
314   i, NewCount: Integer;
315   NewItem: TExternHelpItem;
316 begin
317   Node.Name:=Config.GetValue(Path+'Name','');
318   Node.Filename:=Config.GetValue(Path+'Filename/Value','');
319   Node.WithSubDirectories:=Config.GetValue(Path+'WithSubDirectories/Value',false);
320   Node.URL:=Config.GetValue(Path+'URL/Value','');
321   NewCount:=Config.GetValue(Path+'ChildCount',0);
322   for i:=1 to NewCount do begin
323     NewItem:=TExternHelpItem.Create;
324     NewItem.StoreIn:=Node.StoreIn;
325     Node.AddChild(NewItem);
326     LoadNode(Config,Path+'Item'+IntToStr(i)+'/',NewItem);
327   end;
328 end;
329 
330 procedure TExternHelpOptions.SaveNode(Config: TConfigStorage; Path: string;
331   Node: TExternHelpItem);
332 var
333   i: Integer;
334 begin
335   Config.SetDeleteValue(Path+'Name',Node.Name,'');
336   Config.SetDeleteValue(Path+'Filename/Value',Node.Filename,'');
337   Config.SetDeleteValue(Path+'WithSubDirectories/Value',Node.WithSubDirectories,false);
338   Config.SetDeleteValue(Path+'URL/Value',Node.URL,'');
339   Config.SetDeleteValue(Path+'ChildCount',Node.ChildCount,0);
340   for i:=1 to Node.ChildCount do
341     SaveNode(Config,Path+'Item'+IntToStr(i)+'/',Node.Children[i-1]);
342 end;
343 
344 constructor TExternHelpOptions.Create;
345 var
346   OldHelpDB: THelpDatabase;
347 begin
348   RootItem:=TExternHelpRootItem.Create;
349   RootItem.Owner:=Self;
350   Filename:='externhelp.xml';
351   OldHelpDB:=HelpDatabases.FindDatabase('External help');
352   if OldHelpDB is TExternalHelpDatabase then
353     FHelpDB:=TExternalHelpDatabase(OldHelpDB)
354   else
355     FHelpDB:=TExternalHelpDatabase(HelpDatabases.CreateHelpDatabase('External help',
356                                                TExternalHelpDatabase,true));
357   PackageEditingInterface.AddHandlerOnPackageFileLoaded(@PkgFileLoaded);
358 end;
359 
360 destructor TExternHelpOptions.Destroy;
361 begin
362   FreeAndNil(RootItem);
363   // FHelpDB is freed by the IDE
364   inherited Destroy;
365 end;
366 
367 procedure TExternHelpOptions.Clear;
368 begin
369   RootItem.Clear;
370 end;
371 
372 procedure TExternHelpOptions.ClearItemsStoredInUserSettings;
373 var
374   i: Integer;
375   Item: TExternHelpItem;
376 begin
377   if RootItem<>nil then begin
378     for i:=RootItem.ChildCount-1 downto 0 do begin
379       Item:=RootItem.Children[i];
380       if (Item.StoreIn='') then
381         Item.Free;
382     end;
383   end;
384 end;
385 
386 procedure TExternHelpOptions.ClearItemsStoredInPackages(Parent: TExternHelpItem;
387   const Name: string);
388 var
389   i: Integer;
390   Item: TExternHelpItem;
391 begin
392   if Parent<>nil then begin
393     for i:=Parent.ChildCount-1 downto 0 do begin
394       Item:=Parent.Children[i];
395       if (Item.StoreIn<>'')
396       and (Name='*') or (SysUtils.CompareText(Item.StoreIn,Name)=0) then
397         Item.Free;
398     end;
399   end;
400 end;
401 
402 procedure TExternHelpOptions.LoadOptionsFromPackage(Pkg: TIDEPackage;
403   Parent: TExternHelpItem);
404 var
405   Cnt: integer;
406   i: Integer;
407   Path: String;
408   NewItem: TExternHelpItem;
409 begin
410   if Pkg.Name='' then exit;
411   ClearItemsStoredInPackages(Parent,Pkg.Name);
412   Path:='ExternHelp/';
413   Cnt:=Pkg.CustomOptions.GetValue(Path+'Count',0);
414   //DebugLn(['TExternHelpOptions.LoadOptionsFromPackage ',Pkg.Name,' Cnt=',Cnt]);
415   for i:=1 to Cnt do begin
416     NewItem:=TExternHelpItem.Create;
417     Parent.AddChild(NewItem);
418     NewItem.StoreIn:=Pkg.Name;
419     LoadNode(Pkg.CustomOptions,Path+'Item'+IntToStr(i)+'/',NewItem);
420   end;
421 end;
422 
423 procedure TExternHelpOptions.LoadOptionsFromPackages;
424 var
425   i: Integer;
426 begin
427   if PackageEditingInterface=nil then exit;
428   ClearItemsStoredInPackages(RootItem,'*');
429   for i:=0 to PackageEditingInterface.GetPackageCount-1 do
430     LoadOptionsFromPackage(PackageEditingInterface.GetPackages(i),RootItem);
431 end;
432 
433 procedure TExternHelpOptions.SaveOptionsToPackage(Pkg: TIDEPackage);
434 var
435   Cnt: Integer;
436   Path: String;
437   Item: TExternHelpItem;
438   i: Integer;
439   TmpRoot: TExternHelpItem;
440   Changed: Boolean;
441 begin
442   if Pkg.Name='' then exit;
443   //DebugLn(['TExternHelpOptions.SaveOptionsToPackage START ',Pkg.Name]);
444 
445   // check if something changed
446   TmpRoot:=TExternHelpItem.Create;
447   try
448     LoadOptionsFromPackage(Pkg,TmpRoot);
449     Changed:=false;
450     Cnt:=0;
451     for i:=0 to RootItem.ChildCount-1 do begin
452       Item:=RootItem.Children[i];
453       if SysUtils.CompareText(Item.StoreIn,Pkg.Name)<>0 then continue;
454       if (Cnt=TmpRoot.ChildCount)
455       or (not TmpRoot.Children[Cnt].IsEqual(Item,true)) then begin
456         Changed:=true;
457         break;
458       end;
459       inc(Cnt);
460     end;
461     if TmpRoot.ChildCount>Cnt then Changed:=true;
462     if not Changed then exit;
463   finally
464     TmpRoot.Free;
465   end;
466   DebugLn(['TExternHelpOptions.SaveOptionsToPackage CHANGED: ',Pkg.Name]);
467 
468   // save to package and mark it modified
469   Path:='ExternHelp';
470   Pkg.CustomOptions.DeletePath(Path);
471   Path:=Path+'/';
472   Cnt:=0;
473   for i:=0 to RootItem.ChildCount-1 do begin
474     Item:=RootItem.Children[i];
475     //DebugLn(['TExternHelpOptions.SaveOptionsToPackage ',Item.Name,' StoreIN=',Item.StoreIn,' ',SysUtils.CompareText(Item.StoreIn,Pkg.Name)=0]);
476     if SysUtils.CompareText(Item.StoreIn,Pkg.Name)<>0 then continue;
477     inc(Cnt);
478     SaveNode(Pkg.CustomOptions,Path+'Item'+IntToStr(Cnt)+'/',Item);
479   end;
480   Pkg.CustomOptions.SetDeleteValue(Path+'Count',Cnt,0);
481   Pkg.Modified:=True;
482   //DebugLn(['TExternHelpOptions.SaveOptionsToPackage MODIFIED: ',Pkg.Name]);
483 end;
484 
485 procedure TExternHelpOptions.SaveOptionsToPackages;
486 var
487   i: Integer;
488   Pkg: TIDEPackage;
489 begin
490   if PackageEditingInterface=nil then exit;
491   for i:=0 to PackageEditingInterface.GetPackageCount-1 do begin
492     Pkg:=PackageEditingInterface.GetPackages(i);
493     if Pkg.ReadOnly then continue;
494     SaveOptionsToPackage(Pkg);
495   end;
496 end;
497 
TExternHelpOptions.GetGroupCaptionnull498 class function TExternHelpOptions.GetGroupCaption: string;
499 begin
500   Result:=ehrsGroupTitle;
501 end;
502 
TExternHelpOptions.GetInstancenull503 class function TExternHelpOptions.GetInstance: TAbstractIDEOptions;
504 begin
505   Result:=ExternHelpOptions;
506 end;
507 
Loadnull508 function TExternHelpOptions.Load(Config: TConfigStorage;
509   KeepPackageOpts: boolean): TModalResult;
510 var
511   Path: String;
512 begin
513   Result:=mrOk;
514   if KeepPackageOpts then
515     ClearItemsStoredInUserSettings
516   else
517     Clear;
518   Path:='ExternHelp/';
519   LoadNode(Config,Path+'Items/',RootItem);
520 end;
521 
TExternHelpOptions.Savenull522 function TExternHelpOptions.Save(Config: TConfigStorage): TModalResult;
523 var
524   Path: String;
525   Item: TExternHelpItem;
526   i: Integer;
527   Cnt: Integer;
528 begin
529   Result:=mrOk;
530   Path:='ExternHelp/';
531   Cnt:=0;
532   for i:=1 to RootItem.ChildCount do begin
533     Item:=RootItem.Children[i-1];
534     if Item.StoreIn<>'' then continue;
535     inc(Cnt);
536     SaveNode(Config,Path+'Items/Item'+IntToStr(Cnt)+'/',Item);
537   end;
538   Config.SetDeleteValue(Path+'Items/ChildCount',Cnt,0);
539 end;
540 
TExternHelpOptions.LoadFromFilenull541 function TExternHelpOptions.LoadFromFile(Filename: string;
542   KeepPackageOpts: boolean): TModalResult;
543 var
544   Config: TConfigStorage;
545 begin
546   Config:=GetIDEConfigStorage(Filename,true);
547   try
548     Result:=Load(Config,KeepPackageOpts);
549   finally
550     Config.Free;
551   end;
552 end;
553 
TExternHelpOptions.SaveToFilenull554 function TExternHelpOptions.SaveToFile(Filename: string): TModalResult;
555 var
556   Config: TConfigStorage;
557 begin
558   Config:=GetIDEConfigStorage(Filename,false);
559   try
560     Result:=Save(Config);
561     //DebugLn(['TExternHelpOptions.SaveToFile ',Filename,' ',Config.GetFilename]);
562     Config.WriteToDisk;
563     //DebugLn(['TExternHelpOptions.SaveToFile exists=',FileExistsUTF8(GetFullFilename)]);
564   finally
565     Config.Free;
566   end;
567 end;
568 
Loadnull569 function TExternHelpOptions.Load(KeepPackageOpts: boolean): TModalResult;
570 begin
571   Result:=LoadFromFile(Filename,KeepPackageOpts);
572   FLastSavedChangeStep:=ChangeStep;
573 end;
574 
TExternHelpOptions.Savenull575 function TExternHelpOptions.Save: TModalResult;
576 var
577   FullFilename: String;
578 begin
579   FullFilename:=GetFullFilename;
580   if FileExistsUTF8(FullFilename)
581   and (FLastSavedChangeStep=ChangeStep) then
582     Result:=mrOK;
583   Result:=SaveToFile(Filename);
584   FLastSavedChangeStep:=ChangeStep;
585 end;
586 
GetFullFilenamenull587 function TExternHelpOptions.GetFullFilename: string;
588 begin
589   Result:=Filename;
590   if FilenameIsAbsolute(Result) then exit;
591   Result:=AppendPathDelim(LazarusIDE.GetPrimaryConfigPath)+Result;
592 end;
593 
TExternHelpOptions.IsEqualnull594 function TExternHelpOptions.IsEqual(Src: TExternHelpOptions): boolean;
595 begin
596   Result:=RootItem.IsEqual(Src.RootItem,false);
597 end;
598 
599 procedure TExternHelpOptions.Assign(Src: TExternHelpOptions);
600 begin
601   RootItem.Assign(Src.RootItem,false);
602 end;
603 
604 procedure TExternHelpOptions.IncreaseChangeStep;
605 begin
606   if FChangeStep=high(FChangeStep) then
607     FChangeStep:=low(FChangeStep)
608   else
609     inc(FChangeStep);
610 end;
611 
612 procedure TExternHelpOptions.UpdateHelpDB;
613 
614   procedure RegisterItem(Item: TExternHelpItem);
615   var
616     i: Integer;
617     HelpNode: THelpNode;
618     ItemFilename: String;
619     SrcFilter: THelpDBISourceFile;
620   begin
621     if (Item.Filename<>'') and (Item.URL<>'') then begin
622       ItemFilename:=GetForcedPathDelims(Item.Filename);
623       // create a help node for this topic
624       HelpNode:=THelpNode.CreateURL(HelpDB,Item.Name,Item.URL);
625       // create a filter for the source file(s)
626       if Item.IsDirectory then
627         SrcFilter:=THelpDBISourceDirectory.Create(HelpNode,
628                               ItemFilename,'*.pp;*.pas',Item.WithSubDirectories)
629       else
630         SrcFilter:=THelpDBISourceFile.Create(HelpNode,ItemFilename);
631       HelpDB.RegisterItem(SrcFilter);
632     end;
633 
634     for i:=0 to Item.ChildCount-1 do
635       RegisterItem(Item.Children[i]);
636   end;
637 
638 begin
639   HelpDB.UnregisterAllItems;
640   RegisterItem(RootItem);
641 end;
642 
643 { TExternHelpGeneralOptsFrame }
644 
645 procedure TExternHelpGeneralOptsFrame.ItemsTreeViewEdited(Sender: TObject;
646   Node: TTreeNode; var S: string);
647 begin
648   NameChanged(Node,S,false,true);
649 end;
650 
651 procedure TExternHelpGeneralOptsFrame.FilenameEditEditingDone(Sender: TObject);
652 var
653   s: String;
654   TVNode: TTreeNode;
655   Item: TExternHelpItem;
656   Msg: String;
657   Filename: String;
658 begin
659   TVNode:=ItemsTreeView.Selected;
660   if (TVNode=nil) or (not (TObject(TVNode.Data) is TExternHelpItem)) then exit;
661   Item:=TExternHelpItem(TVNode.Data);
662   s:=FilenameEdit.Text;
663   s:=TrimFilename(s);
664   if s<>Item.Filename then begin
665     Filename:=s;
666     if s='' then begin
667       // ok, allow simple deactivate
668     end else begin
669       ForcePathDelims(Filename);
670       IDEMacros.SubstituteMacros(Filename);
671       Msg:='';
672       if (Filename<>'') and (Filename[length(Filename)]=PathDelim) then begin
673         if not DirPathExists(Filename) then
674           Msg:=Format(ehrsDirectoryNotFound, [Filename]);
675       end else begin
676         if not FileExistsUTF8(Filename) then
677           Msg:=Format(ehrsFileNotFound, [Filename]);
678       end;
679       if Msg<>'' then begin
680         MessageDlg(ehrsWarning, Msg, mtWarning, [mbIgnore], 0);
681       end;
682     end;
683     Item.Filename:=s;
684     if not Item.IsDirectory then
685       Item.WithSubDirectories:=false;
686     WithSubDirsCheckBox.Enabled:=Item.IsDirectory;
687   end;
688 end;
689 
690 procedure TExternHelpGeneralOptsFrame.ItemsTreeViewDragOver(Sender,
691   Source: TObject; X, Y: Integer; State: TDragState; var Accept: Boolean);
692 var
693   TVNode: TTreeNode;
694   InsertType: TTreeViewInsertMarkType;
695 begin
696   if State=dsDragEnter then ;
697   if (Source<>ItemsTreeView) or (FDragNode=nil) then begin
698     Accept:=false;
699     exit;
700   end;
701   ItemsTreeView.GetInsertMarkAt(X,Y,TVNode,InsertType);
702   if (TVNode<>nil) then
703     if (TVNode=FDragNode) or TVNode.HasAsParent(FDragNode)
704     or FDragNode.HasAsParent(TVNode) then begin
705       Accept:=false;
706       exit;
707     end;
708 end;
709 
710 procedure TExternHelpGeneralOptsFrame.AddSpeedButtonClick(Sender: TObject);
711 var
712   SelTVNode: TTreeNode;
713   Item: TExternHelpItem;
714   TVNode: TTreeNode;
715   SelItem: TExternHelpItem;
716 begin
717   SelTVNode:=ItemsTreeView.Selected;
718   Item:=TExternHelpItem.Create;
719   Item.Name:=CreateUniqueName('Item');
720   if (SelTVNode<>nil) and (TObject(SelTVNode.Data) is TExternHelpItem) then
721   begin
722     // init with values of selected node
723     SelItem:=TExternHelpItem(SelTVNode.Data);
724     Item.Filename:=SelItem.Filename;
725     Item.URL:=SelItem.URL;
726     Item.StoreIn:=SelItem.StoreIn;
727     SelItem.Parent.AddChild(Item);
728   end else
729     Options.RootItem.AddChild(Item);
730   TVNode:=ItemsTreeView.Items.AddObject(SelTVNode,Item.Name,Item);
731   ItemsTreeView.Selected:=TVNode;
732 end;
733 
734 procedure TExternHelpGeneralOptsFrame.DeleteSpeedButtonClick(Sender: TObject);
735 var
736   SelTVNode: TTreeNode;
737   Item: TExternHelpItem;
738 begin
739   SelTVNode:=ItemsTreeView.Selected;
740   if (SelTVNode=nil) or (not (TObject(SelTVNode.Data) is TExternHelpItem)) then exit;
741   Item:=TExternHelpItem(SelTVNode.Data);
742   // select next
743   if SelTVNode.GetNext<>nil then
744     ItemsTreeView.Selected:=SelTVNode.GetNext
745   else
746     ItemsTreeView.Selected:=SelTVNode.GetPrev;
747   // delete in treeview
748   SelTVNode.Free;
749   // delete in Options
750   Item.Free;
751 end;
752 
753 procedure TExternHelpGeneralOptsFrame.FileBrowseButtonClick(Sender: TObject);
754 var
755   OpenDialog: TOpenDialog;
756   Filename: String;
757   SelTVNode: TTreeNode;
758   Item: TExternHelpItem;
759 begin
760   OpenDialog:=TOpenDialog.Create(nil);
761   try
762     InitIDEFileDialog(OpenDialog);
763     OpenDialog.Title:=ehrsChooseAPascalUnit;
764     if not OpenDialog.Execute then exit;
765     Filename:=Macrofy(TrimFilename(OpenDialog.FileName));
766     FilenameEdit.Text:=Filename;
767     SelTVNode:=ItemsTreeView.Selected;
768     if (SelTVNode<>nil) and (TObject(SelTVNode.Data) is TExternHelpItem) then
769     begin
770       Item:=TExternHelpItem(SelTVNode.Data);
771       Item.Filename:=Filename;
772     end;
773   finally
774     OpenDialog.Free;
775   end;
776 end;
777 
778 procedure TExternHelpGeneralOptsFrame.FileUseMacrosButtonClick(Sender: TObject);
779 var
780   Filename: String;
781 begin
782   Filename:=TrimFilename(FilenameEdit.Text);
783   FilenameEdit.Text:=Macrofy(Filename);
784 end;
785 
786 procedure TExternHelpGeneralOptsFrame.FilenameEditChange(Sender: TObject);
787 var
788   s: String;
789   TVNode: TTreeNode;
790 begin
791   TVNode:=ItemsTreeView.Selected;
792   if (TVNode=nil) or (not (TObject(TVNode.Data) is TExternHelpItem)) then exit;
793   s:=FilenameEdit.Text;
794   s:=TrimFilename(s);
795   WithSubDirsCheckBox.Enabled:=(s<>'') and (s[length(s)] in ['/','\']);
796 end;
797 
798 procedure TExternHelpGeneralOptsFrame.ItemsTreeViewEditing(Sender: TObject;
799   Node: TTreeNode; var AllowEdit: Boolean);
800 begin
801   if AllowEdit or (Node=nil) then ;
802 end;
803 
804 procedure TExternHelpGeneralOptsFrame.ItemsTreeViewEndDrag(Sender,
805   Target: TObject; X, Y: Integer);
806 begin
807   FDragNode:=nil;
808   if (Target=nil) or (X=0) or (Y=0) then ;
809   ItemsTreeView.Options:=ItemsTreeView.Options-[tvoAutoInsertMark];
810 end;
811 
812 procedure TExternHelpGeneralOptsFrame.ItemsTreeViewMouseMove(Sender: TObject;
813   Shift: TShiftState; X, Y: Integer);
814 var
815   TVNode: TTreeNode;
816 begin
817   if not (ssLeft in Shift) then begin
818     // no left mouse button => stop showing insert mark
819     //if tvoAutoInsertMark in ItemsTreeView.Options then DebugLn(['TExternHelpGeneralOptsFrame.ItemsTreeViewMouseMove no left']);
820     ItemsTreeView.Options:=ItemsTreeView.Options-[tvoAutoInsertMark];
821     FDragNode:=nil;
822   end else if (not ItemsTreeView.Dragging) then begin
823     // left mouse button is presses and not yet dragging => start dragging
824     TVNode:=ItemsTreeView.GetNodeAt(X,Y);
825     if (TVNode<>nil) then begin
826       // a node to drag => start dragging
827       FDragNode:=TVNode;
828       ItemsTreeView.BeginDrag(true);
829       ItemsTreeView.Options:=ItemsTreeView.Options-[tvoAutoInsertMark];
830       //DebugLn(['TExternHelpGeneralOptsFrame.ItemsTreeViewMouseMove START']);
831     end else begin
832       // no node to drag
833       ItemsTreeView.Options:=ItemsTreeView.Options-[tvoAutoInsertMark];
834       FDragNode:=nil;
835       //DebugLn(['TExternHelpGeneralOptsFrame.ItemsTreeViewMouseMove no node']);
836     end;
837   end;
838 end;
839 
840 procedure TExternHelpGeneralOptsFrame.ItemsTreeViewSelectionChanged(
841   Sender: TObject);
842 begin
843   SelectionChanged;
844 end;
845 
846 procedure TExternHelpGeneralOptsFrame.ItemsTreeViewStartDrag(Sender: TObject;
847   var DragObject: TDragObject);
848 begin
849   if DragObject=nil then ;
850   ItemsTreeView.Options:=ItemsTreeView.Options+[tvoAutoInsertMark];
851 end;
852 
853 procedure TExternHelpGeneralOptsFrame.NameEditChange(Sender: TObject);
854 var
855   S: String;
856 begin
857   S:=NameEdit.Text;
858   NameChanged(ItemsTreeView.Selected,S,true,false);
859 end;
860 
861 procedure TExternHelpGeneralOptsFrame.NameEditEditingDone(Sender: TObject);
862 var
863   S: String;
864 begin
865   S:=NameEdit.Text;
866   NameChanged(ItemsTreeView.Selected,S,true,true);
867 end;
868 
869 procedure TExternHelpGeneralOptsFrame.SelEditorFileButtonClick(Sender: TObject);
870 var
871   Selector: TExternHelpFileSelector;
872   i: Integer;
873   Filename: String;
874   Files: TStringList;
875   j: Integer;
876   Filename2: String;
877   SelTVNode: TTreeNode;
878   Item: TExternHelpItem;
879 begin
880   if SourceEditorManagerIntf=nil then exit;
881   Files:=TStringList.Create;
882   Selector:=TExternHelpFileSelector.Create(GetParentForm(Self));
883   try
884     // collect open editor files
885     for i:=0 to SourceEditorManagerIntf.SourceEditorCount-1 do begin
886       Filename:=SourceEditorManagerIntf.SourceEditors[i].FileName;
887       j:=Files.Count-1;
888       while (j>=0) and (CompareFilenames(Files[j],Filename)<>0) do dec(j);
889       if j>=0 then continue;
890       Files.Add(Filename);
891     end;
892     // shorten file names
893     for i:=0 to Files.Count-1 do begin
894       Filename:=ExtractFileName(Files[i]);
895       j:=Files.Count-1;
896       while (j>=0)
897       and ((i=j) or (CompareFilenames(ExtractFileName(Files[j]),Filename)<>0)) do
898         dec(j);
899       if (j<0) then begin
900         // short file is unique => use short file
901         Files[i]:=Filename;
902       end;
903     end;
904     Selector.FileListBox.Items:=Files;
905     Selector.Position:=poOwnerFormCenter;
906     Selector.Caption:=ehrsSelectFile;
907     if Files.Count>0 then
908       Selector.FileListBox.ItemIndex:=0;
909     if (Selector.ShowModal=mrOK) then begin
910       i:=Selector.FileListBox.ItemIndex;
911       if i>=0 then begin
912         Filename2:=Selector.FileListBox.Items[i];
913         for i:=0 to SourceEditorManagerIntf.SourceEditorCount-1 do begin
914           Filename:=SourceEditorManagerIntf.SourceEditors[i].FileName;
915           if (CompareFilenames(Filename2,Filename)=0)
916           or (CompareFilenames(Filename2,ExtractFileName(Filename))=0) then
917           begin
918             Filename:=Macrofy(FileName);
919             FilenameEdit.Text:=Filename;
920             SelTVNode:=ItemsTreeView.Selected;
921             if (SelTVNode<>nil) and (TObject(SelTVNode.Data) is TExternHelpItem)
922             then begin
923               Item:=TExternHelpItem(SelTVNode.Data);
924               Item.Filename:=Filename;
925             end;
926             exit;
927           end;
928         end;
929       end;
930     end;
931   finally
932     Selector.Free;
933     Files.Free;
934   end;
935 end;
936 
937 procedure TExternHelpGeneralOptsFrame.StoreComboBoxEditingDone(Sender: TObject);
938 var
939   S: String;
940 begin
941   S:=StoreComboBox.Text;
942   StoreInChanged(ItemsTreeView.Selected,S,true,true);
943 end;
944 
945 procedure TExternHelpGeneralOptsFrame.URLMemoEditingDone(Sender: TObject);
946 var
947   s: String;
948   TVNode: TTreeNode;
949   Item: TExternHelpItem;
950 begin
951   URLMemo.Lines.Delimiter:=' ';
952   s:=URLMemo.Lines.DelimitedText;
953   TVNode:=ItemsTreeView.Selected;
954   if (TVNode=nil) or (not (TObject(TVNode.Data) is TExternHelpItem)) then exit;
955   Item:=TExternHelpItem(TVNode.Data);
956   s:=Trim(s);
957   if s<>Item.URL then begin
958     Item.URL:=s;
959   end;
960 end;
961 
962 procedure TExternHelpGeneralOptsFrame.WithSubDirsCheckBoxEditingDone(
963   Sender: TObject);
964 var
965   TVNode: TTreeNode;
966   Item: TExternHelpItem;
967 begin
968   TVNode:=ItemsTreeView.Selected;
969   if (TVNode=nil) or (not (TObject(TVNode.Data) is TExternHelpItem)) then exit;
970   Item:=TExternHelpItem(TVNode.Data);
971   Item.WithSubDirectories:=WithSubDirsCheckBox.Checked;
972 end;
973 
974 procedure TExternHelpGeneralOptsFrame.FillItemsTreeView;
975 
976   procedure Add(ParentItem: TExternHelpItem; ParentTVNode: TTreeNode);
977   var
978     i: Integer;
979     Item: TExternHelpItem;
980     TVNode: TTreeNode;
981   begin
982     for i:=0 to ParentItem.ChildCount-1 do begin
983       Item:=ParentItem.Children[i];
984       TVNode:=ItemsTreeView.Items.AddChildObject(ParentTVNode,Item.Name,Item);
985       Add(Item,TVNode);
986       TVNode.Expanded:=true;
987     end;
988   end;
989 
990 begin
991   ItemsTreeView.BeginUpdate;
992   ItemsTreeView.Items.Clear;
993   Add(Options.RootItem,nil);
994   ItemsTreeView.EndUpdate;
995 end;
996 
997 procedure TExternHelpGeneralOptsFrame.NameChanged(TVNode: TTreeNode;
998   var NewName: string; UpdateTree, UpdateEdit: boolean);
999 var
1000   Item: TExternHelpItem;
1001 begin
1002   NewName:=Trim(NewName);
1003   if (TVNode<>nil) and (TObject(TVNode.Data) is TExternHelpItem) then begin
1004     Item:=TExternHelpItem(TVNode.Data);
1005     Item.Name:=NewName;
1006     if UpdateTree then
1007       TVNode.Text:=NewName;
1008     if UpdateEdit then
1009       NameEdit.Text:=NewName;
1010   end;
1011 end;
1012 
1013 procedure TExternHelpGeneralOptsFrame.StoreInChanged(TVNode: TTreeNode; var
1014   NewStoreIn: string; UpdateTree, UpdateEdit: boolean);
1015 var
1016   Item: TExternHelpItem;
1017 begin
1018   NewStoreIn:=Trim(NewStoreIn);
1019   if NewStoreIn=FMySettingsCaption then
1020     NewStoreIn:='';
1021   if (TVNode<>nil) and (TObject(TVNode.Data) is TExternHelpItem) then begin
1022     Item:=TExternHelpItem(TVNode.Data);
1023     Item.StoreIn:=NewStoreIn;
1024     if UpdateTree then
1025       ;
1026     if UpdateEdit then
1027       StoreComboBox.Text:=NewStoreIn;
1028   end;
1029 end;
1030 
1031 procedure TExternHelpGeneralOptsFrame.SelectionChanged;
1032 var
1033   TVNode: TTreeNode;
1034   Item: TExternHelpItem;
1035   s: String;
1036   ItemFilename: String;
1037 begin
1038   TVNode:=ItemsTreeView.Selected;
1039   Item:=nil;
1040   if (TVNode<>nil) and (TObject(TVNode.Data) is TExternHelpItem) then
1041     Item:=TExternHelpItem(TVNode.Data);
1042   DisableAlign;
1043   try
1044     if Item<>nil then begin
1045       NameEdit.Enabled:=true;
1046       NameEdit.Text:=Item.Name;
1047       FilenameEdit.Enabled:=true;
1048       ItemFilename:=GetForcedPathDelims(Item.Filename);
1049       FilenameEdit.Text:=ItemFilename;
1050       WithSubDirsCheckBox.Enabled:=Item.IsDirectory;
1051       WithSubDirsCheckBox.Checked:=Item.WithSubDirectories;
1052       URLMemo.Enabled:=true;
1053       URLMemo.Lines.Text:=Item.URL;
1054       StoreComboBox.Enabled:=Item.Parent=Options.RootItem;
1055       s:=Item.StoreIn;
1056       if s='' then s:=FMySettingsCaption;
1057       StoreComboBox.Text:=s;
1058     end else begin
1059       NameEdit.Enabled:=false;
1060       NameEdit.Text:='';
1061       FilenameEdit.Enabled:=false;
1062       FilenameEdit.Text:='';
1063       WithSubDirsCheckBox.Enabled:=false;
1064       WithSubDirsCheckBox.Checked:=false;
1065       URLMemo.Enabled:=false;
1066       URLMemo.Lines.Text:='';
1067       StoreComboBox.Enabled:=false;
1068       StoreComboBox.Text:='';
1069     end;
1070   finally
1071     EnableAlign;
1072   end;
1073 end;
1074 
FindTVNodenull1075 function TExternHelpGeneralOptsFrame.FindTVNode(NodeText: string): TTreeNode;
1076 begin
1077   Result:=ItemsTreeView.Items.GetFirstNode;
1078   while (Result<>nil) and (SysUtils.CompareText(Result.Text,NodeText)<>0) do
1079     Result:=Result.GetNext;
1080 end;
1081 
CreateUniqueNamenull1082 function TExternHelpGeneralOptsFrame.CreateUniqueName(Prefix: string): string;
1083 var
1084   i: Integer;
1085 begin
1086   i:=0;
1087   repeat
1088     inc(i);
1089     Result:=Prefix+IntToStr(i);
1090   until FindTVNode(Result)=nil;
1091 end;
1092 
1093 procedure TExternHelpGeneralOptsFrame.FillStoreInCombobox;
1094 var
1095   sl: TStringList;
1096   i: Integer;
1097 begin
1098   sl:=TStringList.Create;
1099   try
1100     for i:=0 to PackageEditingInterface.GetPackageCount-1 do
1101       sl.Add(PackageEditingInterface.GetPackages(i).Name);
1102     sl.Sort;
1103     sl.Insert(0, Trim(FMySettingsCaption));
1104     StoreComboBox.Items.Assign(sl);
1105   finally
1106     sl.Free;
1107   end;
1108 end;
1109 
Macrofynull1110 function TExternHelpGeneralOptsFrame.Macrofy(Filename: string): string;
1111 
1112   procedure CheckPath(PathMacro: string; var s: string);
1113   var
1114     Value: String;
1115   begin
1116     Value:=PathMacro;
1117     if (not IDEMacros.SubstituteMacros(Value)) or (Value='')
1118     or (Value[1]='$') then
1119       exit;
1120     Value:=ChompPathDelim(Value);
1121     if (CompareFilenames(copy(s,1,length(Value)),Value)=0)
1122     and ((length(s)<=length(Value)) or (s[length(Value)+1]=PathDelim)) then
1123     begin
1124       // filename is a file in the macro path
1125       s:=PathMacro+copy(s,length(Value)+1,length(s));
1126       exit;
1127     end;
1128   end;
1129 
1130 begin
1131   Result:=Filename;
1132   CheckPath('$(FPCSrcDir)',Result);
1133   CheckPath('$(LazarusDir)',Result);
1134 end;
1135 
1136 constructor TExternHelpGeneralOptsFrame.Create(TheOwner: TComponent);
1137 begin
1138   inherited Create(TheOwner);
1139   FOptions:=TExternHelpOptions.Create;
1140   IDEImages.AssignImage(AddSpeedButton, 'laz_add');
1141   IDEImages.AssignImage(DeleteSpeedButton, 'laz_delete');
1142 end;
1143 
1144 destructor TExternHelpGeneralOptsFrame.Destroy;
1145 begin
1146   FreeAndNil(FOptions);
1147   inherited Destroy;
1148 end;
1149 
TExternHelpGeneralOptsFrame.GetTitlenull1150 function TExternHelpGeneralOptsFrame.GetTitle: String;
1151 begin
1152   Result:=ehrsExternal;
1153 end;
1154 
1155 procedure TExternHelpGeneralOptsFrame.ReadSettings(AOptions: TAbstractIDEOptions);
1156 begin
1157   if not (AOptions is TAbstractIDEHelpOptions) then exit;
1158   Options.Assign(ExternHelpOptions);
1159   FillStoreInCombobox;
1160   FillItemsTreeView;
1161   ItemsTreeView.Selected:=ItemsTreeView.Items.GetFirstNode;
1162   SelectionChanged;
1163 end;
1164 
1165 procedure TExternHelpGeneralOptsFrame.Setup(ADialog: TAbstractOptionsEditorDialog);
1166 begin
1167   if ADialog=nil then ;
1168   NameLabel.Caption:=ehrsName;
1169   FilenameLabel.Caption:=ehrsUnitFileOrUnitDirectory;
1170   URLLabel.Caption:=ehrsURL;
1171   HelpBitBtn.Caption:=ehrsHelp;
1172   AddSpeedButton.Hint:=ehrsAddNewItem;
1173   DeleteSpeedButton.Hint:=ehrsDeleteItem;
1174   FileBrowseButton.Caption:=ehrsBrowse;
1175   FileBrowseButton.Hint:=ehrsBrowseForPath;
1176   FileUseMacrosButton.Caption:=ehrsMacrofy;
1177   FileUseMacrosButton.Hint:=ehrsReplaceCommonDirectoriesWithMacros;
1178   SelEditorFileButton.Caption:=ehrsEditorFile;
1179   SelEditorFileButton.Hint:=ehrsSelectAFileFromTheSourceEditor;
1180   WithSubDirsCheckBox.Caption:=ehrsIncludeSubDirectories;
1181   StoreLabel.Caption:=ehrsStoreThisURLIn;
1182   FMySettingsCaption:=ehrsMySettings;
1183 end;
1184 
TExternHelpGeneralOptsFrame.SupportedOptionsClassnull1185 class function TExternHelpGeneralOptsFrame.SupportedOptionsClass: TAbstractIDEOptionsClass;
1186 begin
1187   Result:=TAbstractIDEHelpOptions;
1188 end;
1189 
1190 procedure TExternHelpGeneralOptsFrame.WriteSettings(AOptions: TAbstractIDEOptions);
1191 begin
1192   if not (AOptions is TAbstractIDEHelpOptions) then exit;
1193   if ExternHelpOptions.IsEqual(Options) then exit;
1194   ExternHelpOptions.Assign(Options);
1195   try
1196     ExternHelpOptions.Save;
1197   except
1198     on E: Exception do begin
1199       DebugLn(['TExternHelpGeneralOptsFrame.WriteSettings unable to write file ',ExternHelpOptions.Filename,': ',E.Message]);
1200     end;
1201   end;
1202   ExternHelpOptions.SaveOptionsToPackages;
1203   ExternHelpOptions.UpdateHelpDB;
1204 end;
1205 
1206 { TExternHelpItem }
1207 
GetChildCountnull1208 function TExternHelpItem.GetChildCount: integer;
1209 begin
1210   Result:=fChilds.Count;
1211 end;
1212 
TExternHelpItem.GetChildrennull1213 function TExternHelpItem.GetChildren(Index: integer): TExternHelpItem;
1214 begin
1215   Result:=TExternHelpItem(fChilds[Index]);
1216 end;
1217 
1218 procedure TExternHelpItem.SetFilename(const AValue: string);
1219 begin
1220   if FFilename=AValue then exit;
1221   FFilename:=AValue;
1222   IncreaseChangeStep;
1223 end;
1224 
1225 procedure TExternHelpItem.SetName(const AValue: string);
1226 begin
1227   if FName=AValue then exit;
1228   FName:=AValue;
1229   IncreaseChangeStep;
1230 end;
1231 
1232 procedure TExternHelpItem.SetStoreIn(const AValue: string);
1233 begin
1234   if FStoreIn=AValue then exit;
1235   FStoreIn:=AValue;
1236   IncreaseChangeStep;
1237 end;
1238 
1239 procedure TExternHelpItem.SetURL(const AValue: string);
1240 begin
1241   if FURL=AValue then exit;
1242   FURL:=AValue;
1243   IncreaseChangeStep;
1244 end;
1245 
1246 procedure TExternHelpItem.SetWithSubDirectories(const AValue: boolean);
1247 begin
1248   if FWithSubDirectories=AValue then exit;
1249   FWithSubDirectories:=AValue;
1250   IncreaseChangeStep;
1251 end;
1252 
1253 constructor TExternHelpItem.Create;
1254 begin
1255   fChilds:=TFPList.Create;
1256 end;
1257 
1258 destructor TExternHelpItem.Destroy;
1259 begin
1260   if Parent<>nil then
1261     Parent.RemoveChild(Parent.IndexOf(Self));
1262   Clear;
1263   FreeAndNil(fChilds);
1264   inherited Destroy;
1265 end;
1266 
1267 procedure TExternHelpItem.Clear;
1268 var
1269   i: Integer;
1270   Child: TExternHelpItem;
1271 begin
1272   if (ChildCount=0) and (URL='') and (Filename='') and (StoreIn='') then exit;
1273   for i:=fChilds.Count-1 downto 0 do begin
1274     Child:=Children[i];
1275     Child.Parent:=nil;
1276     Child.Free;
1277   end;
1278   fChilds.Clear;
1279   fURL:='';
1280   FFilename:='';
1281   FWithSubDirectories:=false;
1282   FStoreIn:='';
1283   IncreaseChangeStep;
1284 end;
1285 
1286 procedure TExternHelpItem.AddChild(Item: TExternHelpItem);
1287 begin
1288   Item.Parent:=Self;
1289   fChilds.Add(Item);
1290   IncreaseChangeStep;
1291 end;
1292 
1293 procedure TExternHelpItem.MoveChild(FromPos, ToPos: integer);
1294 begin
1295   if FromPos=ToPos then exit;
1296   fChilds.Move(FromPos,ToPos);
1297   IncreaseChangeStep;
1298 end;
1299 
1300 procedure TExternHelpItem.RemoveChild(Index: integer);
1301 begin
1302   Children[Index].Parent:=nil;
1303   fChilds.Delete(Index);
1304   IncreaseChangeStep;
1305 end;
1306 
1307 procedure TExternHelpItem.DeleteChild(Index: integer);
1308 begin
1309   Children[Index].Free;
1310 end;
1311 
1312 procedure TExternHelpItem.DeleteChild(Child: TExternHelpItem);
1313 begin
1314   Child.Free;
1315 end;
1316 
IndexOfnull1317 function TExternHelpItem.IndexOf(Child: TExternHelpItem): integer;
1318 begin
1319   Result:=fChilds.IndexOf(Child);
1320 end;
1321 
TExternHelpItem.IsEqualnull1322 function TExternHelpItem.IsEqual(Item: TExternHelpItem; WithName: boolean
1323   ): boolean;
1324 var
1325   i: Integer;
1326 begin
1327   Result:=((not WithName) or (Name=Item.Name))
1328     and (Filename=Item.Filename)
1329     and (WithSubDirectories=Item.WithSubDirectories)
1330     and (URL=Item.URL)
1331     and (StoreIn=Item.StoreIn)
1332     and (ChildCount=Item.ChildCount);
1333   if Result then begin
1334     for i:=0 to ChildCount-1 do
1335       if not (Children[i].IsEqual(Item.Children[i],true)) then exit(false);
1336   end;
1337 end;
1338 
IsDirectorynull1339 function TExternHelpItem.IsDirectory: boolean;
1340 begin
1341   Result:=(Filename<>'') and (Filename[length(Filename)] in ['/','\']);
1342 end;
1343 
1344 procedure TExternHelpItem.Assign(Src: TExternHelpItem; WithName: boolean);
1345 var
1346   i: Integer;
1347   Item: TExternHelpItem;
1348 begin
1349   if WithName then Name:=Src.Name;
1350   Filename:=Src.Filename;
1351   WithSubDirectories:=Src.WithSubDirectories;
1352   URL:=Src.URL;
1353   StoreIn:=Src.StoreIn;
1354   for i:=0 to Src.ChildCount-1 do begin
1355     if ChildCount<=i then begin
1356       Item:=TExternHelpItem.Create;
1357       AddChild(Item);
1358       IncreaseChangeStep;
1359     end else begin
1360       Item:=Children[i];
1361     end;
1362     Item.Assign(Src.Children[i],true);
1363   end;
1364   while ChildCount>Src.ChildCount do begin
1365     DeleteChild(ChildCount-1);
1366     IncreaseChangeStep;
1367   end;
1368 end;
1369 
1370 procedure TExternHelpItem.IncreaseChangeStep;
1371 begin
1372   if Parent<>nil then Parent.IncreaseChangeStep;
1373   if FChangeStep=High(FChangeStep) then
1374     FChangeStep:=low(FChangeStep)
1375   else
1376     inc(FChangeStep);
1377 end;
1378 
1379 { TExternHelpRootItem }
1380 
1381 procedure TExternHelpRootItem.IncreaseChangeStep;
1382 begin
1383   inherited IncreaseChangeStep;
1384   Owner.IncreaseChangeStep;
1385 end;
1386 
1387 { TExternalHelpDatabase }
1388 
1389 constructor TExternalHelpDatabase.Create(TheOwner: TComponent);
1390 begin
1391   inherited Create(TheOwner);
1392 end;
1393 
1394 destructor TExternalHelpDatabase.Destroy;
1395 begin
1396   inherited Destroy;
1397 end;
1398 
TExternalHelpDatabase.ShowHelpnull1399 function TExternalHelpDatabase.ShowHelp(Query: THelpQuery; BaseNode,
1400   NewNode: THelpNode; QueryItem: THelpQueryItem; var ErrMsg: string
1401   ): TShowHelpResult;
1402 var
1403   ContextList: TPascalHelpContextList;
1404   Identifier: String;
1405   AUnitName: String;
1406   i: Integer;
1407   Context: String;
1408   p: LongInt;
1409   URL: String;
1410   ShowNode: THelpNode;
1411   Viewer: THelpViewer;
1412 begin
1413   //DebugLn(['TExternalHelpDatabase.ShowHelp ',DbgSName(Query)]);
1414   if (Query is THelpQueryPascalContexts)
1415   and (QueryItem is TPascalHelpContextList) then begin
1416     // a pascal context query
1417     ContextList:=TPascalHelpContextList(QueryItem);
1418     if (ContextList.Count>0) and (ContextList.List[0].Descriptor=pihcFilename)
1419     then begin
1420       // extract unit filename
1421       AUnitName:=lowercase(ExtractFileNameOnly(ContextList.List[0].Context));
1422       DebugLn('TExternalHelpDatabase.ShowHelp A Unitname=',AUnitname,' NewNode.HelpType=',dbgs(ord(NewNode.HelpType)),' NewNode.Title=',NewNode.Title,' NewNode.URL=',NewNode.URL);
1423       if AUnitName<>'' then begin
1424 
1425         // extract identifier
1426         Identifier:='';
1427         for i:=0 to ContextList.Count-1 do begin
1428           Context:=ContextList.List[i].Context;
1429           case ContextList.List[i].Descriptor of
1430 
1431           pihcProperty,pihcVariable,pihcType,pihcConst:
1432             begin
1433               Identifier:=Context;
1434               break;
1435             end;
1436 
1437           pihcProcedure:
1438             begin
1439               // chomp parameters  ToDo: overloaded procs
1440               p:=System.Pos('(',Context);
1441               if p>0 then
1442                 Context:=copy(Context,1,p-1);
1443               Identifier:=Context;
1444               break;
1445             end;
1446 
1447           end;
1448         end;
1449 
1450         if Identifier<>'' then begin
1451           DebugLn(['TExternalHelpDatabase.ShowHelp Identifier=',Identifier]);
1452           // replace special macros (Identifier)
1453           URL:=NewNode.URL;
1454           repeat
1455             p:=System.Pos('$(identifier)',lowercase(URL));
1456             if p<1 then break;
1457             URL:=copy(URL,1,p-1)+Identifier
1458                                +copy(URL,p+length('$(identifier)'),length(URL));
1459           until false;
1460 
1461           // replace global macros
1462           if (IDEMacros<>nil) then
1463             IDEMacros.SubstituteMacros(URL);
1464 
1465           DebugLn(['TExternalHelpDatabase.ShowHelp URL=',URL]);
1466 
1467           // find HTML viewer
1468           Result:=FindViewer('text/html',ErrMsg,Viewer);
1469           if Result<>shrSuccess then exit;
1470 
1471           // call viewer
1472           ShowNode:=nil;
1473           try
1474             ShowNode:=THelpNode.CreateURL(Self,NewNode.Title,URL);
1475             Result:=Viewer.ShowNode(ShowNode,ErrMsg);
1476           finally
1477             ShowNode.Free;
1478           end;
1479           exit;
1480         end;
1481       end;
1482     end;
1483   end;
1484 
1485   // otherwise use default
1486   Result:=inherited ShowHelp(Query, BaseNode, NewNode, QueryItem, ErrMsg);
1487 end;
1488 
1489 { TExternHelpFileSelector }
1490 
1491 procedure TExternHelpFileSelector.ButtonPanel1OKButtonClick(Sender: TObject);
1492 begin
1493   ModalResult:=mrOk;
1494 end;
1495 
1496 constructor TExternHelpFileSelector.Create(TheOwner: TComponent);
1497 begin
1498   inherited CreateNew(TheOwner);
1499 
1500   FileListBox:=TListBox.Create(Self);
1501   with FileListBox do begin
1502     Name:='FileListBox';
1503     Align:=alClient;
1504     BorderSpacing.Around:=6;
1505     Parent:=Self;
1506   end;
1507 
1508   ButtonPanel1:=TButtonPanel.Create(Self);
1509   with ButtonPanel1 do begin
1510     Name:='ButtonPanel1';
1511     Align:=alBottom;
1512     ShowBevel:=false;
1513     ShowButtons:=[pbOK,pbCancel];
1514     OKButton.OnClick:=@ButtonPanel1OKButtonClick;
1515     OKButton.ModalResult:=mrNone;
1516     Parent:=Self;
1517   end;
1518 end;
1519 
1520 finalization
1521   FreeAndNil(ExternHelpOptions);
1522 
1523 end.
1524 
1525