1 {
2 /***************************************************************************
3 helpmanager.pas
4 ---------------
5
6
7 ***************************************************************************/
8
9 ***************************************************************************
10 * *
11 * This source is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 * This code is distributed in the hope that it will be useful, but *
17 * WITHOUT ANY WARRANTY; without even the implied warranty of *
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
19 * General Public License for more details. *
20 * *
21 * A copy of the GNU General Public License is available on the World *
22 * Wide Web at <http://www.gnu.org/copyleft/gpl.html>. You can also *
23 * obtain it by writing to the Free Software Foundation, *
24 * Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1335, USA. *
25 * *
26 ***************************************************************************
27 }
28 unit IDEHelpManager;
29
30 {$mode objfpc}{$H+}
31
32 interface
33
34 uses
35 // RTL + FCL
36 Classes, SysUtils, Laz_AVL_Tree,
37 // LCL
38 LCLProc, LCLIntf, LCLType, FileProcs, Forms, Controls, ComCtrls, StdCtrls,
39 Dialogs, Graphics, Buttons, ButtonPanel, LazHelpHTML, HelpIntfs,
40 // LazUtils
41 LConvEncoding, LazFileUtils, HTML2TextRender,
42 // CodeTools
43 BasicCodeTools, CodeToolManager, CodeCache, CustomCodeTool, CodeTree,
44 PascalParserTool, FindDeclarationTool,
45 // IDEIntf
46 PropEdits, ObjectInspector, TextTools, IDEDialogs, LazHelpIntf, MacroIntf,
47 IDEWindowIntf, IDEMsgIntf, PackageIntf, LazIDEIntf, HelpFPDoc, IDEHelpIntf,
48 IDEExternToolIntf, IDEImagesIntf,
49 // IDE
50 LazarusIDEStrConsts, DialogProcs, ObjInspExt, EnvironmentOpts, AboutFrm,
51 Project, MainBar, IDEFPDocFileSearch, PackageDefs, PackageSystem, HelpOptions,
52 MainIntf, LazConf, HelpFPCMessages, CodeHelp, IDEWindowHelp, CodeBrowser;
53
54 type
55
56 { TSimpleFPCKeywordHelpDatabase }
57
58 TSimpleFPCKeywordHelpDatabase = class(THTMLHelpDatabase)
59 private
60 FKeywordPrefixNode: THelpNode;
61 public
GetNodesForKeywordnull62 function GetNodesForKeyword(const HelpKeyword: string;
63 var ListOfNodes: THelpNodeQueryList; var {%H-}ErrMsg: string
64 ): TShowHelpResult; override;
ShowHelpnull65 function ShowHelp(Query: THelpQuery; {%H-}BaseNode, {%H-}NewNode: THelpNode;
66 {%H-}QueryItem: THelpQueryItem;
67 var ErrMsg: string): TShowHelpResult; override;
68 end;
69
70 TLIHProviders = class;
71
72 { TLazIDEHTMLProvider }
73
74 TLazIDEHTMLProvider = class(TAbstractIDEHTMLProvider)
75 private
76 fWaitingForAsync: boolean;
77 FProviders: TLIHProviders;
78 procedure SetProviders(const AValue: TLIHProviders);
79 procedure OpenNextURL({%H-}Data: PtrInt); // called via Application.QueueAsyncCall
80 procedure OpenFPDoc(Path: string);
81 public
82 NextURL: string;
83 destructor Destroy; override;
URLHasStreamnull84 function URLHasStream(const URL: string): boolean; override;
85 procedure OpenURLAsync(const URL: string); override;
GetStreamnull86 function GetStream(const URL: string; Shared: Boolean): TStream; override;
87 procedure ReleaseStream(const URL: string); override;
88 property Providers: TLIHProviders read FProviders write SetProviders;
89 end;
90
91 { TLIHProviderStream }
92
93 TLIHProviderStream = class
94 private
95 FRefCount: integer;
96 public
97 Stream: TStream;
98 URL: string;
99 destructor Destroy; override;
100 procedure IncreaseRefCount;
101 procedure DecreaseRefCount;
102 property RefCount: integer read FRefCount;
103 end;
104
105 { TLIHProviders
106 manages all TLazIDEHTMLProvider }
107
108 TLIHProviders = class
109 private
110 FStreams: TAVLTree;// tree of TLIHProviderStream sorted for URL
111 public
112 constructor Create;
113 destructor Destroy; override;
FindStreamnull114 function FindStream(const URL: string; CreateIfNotExists: Boolean): TLIHProviderStream;
GetStreamnull115 function GetStream(const URL: string; Shared: boolean): TStream;
116 procedure ReleaseStream(const URL: string);
117 end;
118
119 { TSimpleHTMLControl }
120
121 TSimpleHTMLControl = class(TLabel,TIDEHTMLControlIntf)
122 private
123 FMaxLineCount: integer;
124 FProvider: TAbstractIDEHTMLProvider;
125 FURL: string;
126 procedure SetProvider(const AValue: TAbstractIDEHTMLProvider);
127 public
128 constructor Create(AOwner: TComponent); override;
GetURLnull129 function GetURL: string;
130 procedure SetURL(const AValue: string);
131 property Provider: TAbstractIDEHTMLProvider read FProvider write SetProvider;
132 procedure SetHTMLContent(Stream: TStream; const NewURL: string);
133 procedure GetPreferredControlSize(out AWidth, AHeight: integer);
134 property MaxLineCount: integer read FMaxLineCount write FMaxLineCount;
135 end;
136
137 { TScrollableHTMLControl }
138
139 TScrollableHTMLControl = class(TMemo,TIDEHTMLControlIntf)
140 private
141 FProvider: TAbstractIDEHTMLProvider;
142 FURL: string;
143 procedure SetProvider(const AValue: TAbstractIDEHTMLProvider);
144 public
145 constructor Create(AOwner: TComponent); override;
GetURLnull146 function GetURL: string;
147 procedure SetURL(const AValue: string);
148 property Provider: TAbstractIDEHTMLProvider read FProvider write SetProvider;
149 procedure SetHTMLContent(Stream: TStream; const NewURL: string);
150 procedure GetPreferredControlSize(out AWidth, AHeight: integer);
151 end;
152
153 { TIDEHelpDatabases }
154
155 TIDEHelpDatabases = class(THelpDatabases)
156 public
ShowHelpSelectornull157 function ShowHelpSelector({%H-}Query: THelpQuery; Nodes: THelpNodeQueryList;
158 var {%H-}ErrMsg: string;
159 var Selection: THelpNodeQuery
160 ): TShowHelpResult; override;
GetBaseDirectoryForBasePathObjectnull161 function GetBaseDirectoryForBasePathObject(BasePathObject: TObject): string; override;
ShowHelpForSourcePositionnull162 function ShowHelpForSourcePosition(Query: THelpQuerySourcePosition;
163 var ErrMsg: string): TShowHelpResult; override;
SubstituteMacrosnull164 function SubstituteMacros(var s: string): boolean; override;
165 end;
166
167
168 { TIDEHelpManager }
169
170 TIDEHelpManager = class(TBaseHelpManager)
171 // help menu of the IDE menu bar
172 procedure mnuHelpAboutLazarusClicked(Sender: TObject);
173 procedure mnuHelpOnlineHelpClicked(Sender: TObject);
174 procedure mnuHelpReportBugClicked(Sender: TObject);
175 // fpdoc
176 procedure mnuSearchInFPDocFilesClick(Sender: TObject);
177 // messages
178 procedure mnuEditMessageHelpClick(Sender: TObject);
179 private
180 FFCLHelpDB: THelpDatabase;
181 FFCLHelpDBPath: THelpBaseURLObject;
182 FHTMLProviders: TLIHProviders;
183 FLCLHelpDB: THelpDatabase;
184 FLCLHelpDBPath: THelpBaseURLObject;
185 FMainHelpDB: THelpDatabase;
186 FMainHelpDBPath: THelpBasePathObject;
187 FRTLHelpDB: THelpDatabase;
188 FRTLHelpDBPath: THelpBaseURLObject;
189 FLazUtilsHelpDB: THelpDatabase;
190 FLazUtilsHelpDBPath: THelpBaseURLObject;
191
192 procedure RegisterIDEHelpDatabases;
193 procedure RegisterDefaultIDEHelpViewers;
194 procedure FindDefaultBrowser(var DefaultBrowser, Params: string);
CollectKeywordsnull195 function CollectKeywords(CodeBuffer: TCodeBuffer; const CodePos: TPoint;
196 out Identifier: string): TShowHelpResult;
CollectDeclarationsnull197 function CollectDeclarations(CodeBuffer: TCodeBuffer; const CodePos: TPoint;
198 out Complete: boolean; var ErrMsg: string): TShowHelpResult;
199 public
200 constructor Create(TheOwner: TComponent); override;
201 destructor Destroy; override;
202
203 procedure ConnectMainBarEvents; override;
204 procedure LoadHelpOptions; override;
205 procedure SaveHelpOptions; override;
206
207 procedure ShowLazarusHelpStartPage;
208 procedure ShowIDEHelpForContext(HelpContext: THelpContext);
209 procedure ShowIDEHelpForKeyword(const Keyword: string); // an arbitrary keyword, not an FPC keyword
210
ShowHelpForSourcePositionnull211 function ShowHelpForSourcePosition(const Filename: string;
212 const CodePos: TPoint;
213 var ErrMsg: string): TShowHelpResult; override;
214 procedure ShowHelpForMessage; override;
215 procedure ShowHelpForObjectInspector(Sender: TObject); override;
216 procedure ShowHelpForIDEControl(Sender: TControl); override;
GetHintForSourcePositionnull217 function GetHintForSourcePosition(const ExpandedFilename: string;
218 const CodePos: TPoint; out BaseURL, HTMLHint: string;
219 Flags: TIDEHelpManagerCreateHintFlags = []): TShowHelpResult; override;
ConvertSourcePosToPascalHelpContextnull220 function ConvertSourcePosToPascalHelpContext(const CaretPos: TPoint;
221 const Filename: string): TPascalHelpContextList; override;
ConvertCodePosToPascalHelpContextnull222 function ConvertCodePosToPascalHelpContext(
223 ACodePos: PCodeXYPosition): TPascalHelpContextList;
GetFPDocFilenameForSourcenull224 function GetFPDocFilenameForSource(SrcFilename: string;
225 ResolveIncludeFiles: Boolean; out AnOwner: TObject): string; override;
226 public
227 property FCLHelpDB: THelpDatabase read FFCLHelpDB;
228 property FCLHelpDBPath: THelpBaseURLObject read FFCLHelpDBPath;
229 property MainHelpDB: THelpDatabase read FMainHelpDB;
230 property MainHelpDBPath: THelpBasePathObject read FMainHelpDBPath;
231 property LCLHelpDB: THelpDatabase read FLCLHelpDB;
232 property LCLHelpDBPath: THelpBaseURLObject read FLCLHelpDBPath;
233 property RTLHelpDB: THelpDatabase read FRTLHelpDB;
234 property RTLHelpDBPath: THelpBaseURLObject read FRTLHelpDBPath;
235 property LazUtilsHelpDB: THelpDatabase read FLazUtilsHelpDB;
236 property LazUtilsHelpDBPath: THelpBaseURLObject read FLazUtilsHelpDBPath;
237 end;
238
239 { TIDEHintWindowManager }
240
241 TIDEHintWindowManager = class(THintWindowManager)
242 public
HintIsComplexnull243 function HintIsComplex: boolean;
SenderIsHintControlnull244 function SenderIsHintControl(Sender: TObject): Boolean;
PtIsOnHintnull245 function PtIsOnHint(Pt: TPoint): boolean;
246 end;
247
248 { THelpSelectorDialog }
249
250 THelpSelectorDialog = class(TForm)
251 BtnPanel: TButtonPanel;
252 NodesGroupBox: TGroupBox;
253 NodesTreeView: TTreeView;
254 procedure HelpSelectorDialogClose(Sender: TObject; var {%H-}CloseAction: TCloseAction);
255 procedure NodesTreeViewDblClick(Sender: TObject);
256 procedure NodesTreeViewSelectionChanged(Sender: TObject);
257 private
258 FNodes: THelpNodeQueryList;
259 FImgIndexDB, FImgIndexNode: Integer;
260 procedure SetNodes(const AValue: THelpNodeQueryList);
261 procedure FillNodesTV;
262 procedure UpdateButtons;
263 public
264 constructor Create(TheOwner: TComponent); override;
265 property Nodes: THelpNodeQueryList read FNodes write SetNodes;
GetSelectedNodeQuerynull266 function GetSelectedNodeQuery: THelpNodeQuery;
267 end;
268
269 { Help Contexts for IDE help }
270 const
271 lihcStartPage = 'StartPage';
272 lihcRTLUnits = 'RTLUnits';
273 lihcFCLUnits = 'FCLUnits';
274 lihcLCLUnits = 'LCLUnits';
275 lihcLazUtilsUnits = 'LazUtilsUnits';
276
277
278 lihBaseUrl = 'http://lazarus-ccr.sourceforge.net/docs/';
279
280 lihRTLURL = lihBaseUrl+'rtl/';
281 lihFCLURL = lihBaseUrl+'fcl/';
282 lihLCLURL = lihBaseUrl+'lcl/';
283
284 lihLazUtilsURL = 'lazutils.chm://';
285 // not important see: ../components/chmhelp/packages/idehelp/lazchmhelp.pas
286
287 var
288 HelpBoss: TBaseHelpManager = nil;
289
290 implementation
291
292 {$R *.lfm}
293
294 // Default help control generator if no other is registered.
LazCreateIDEHTMLControlnull295 function LazCreateIDEHTMLControl(Owner: TComponent;
296 var Provider: TAbstractIDEHTMLProvider;
297 Flags: TIDEHTMLControlFlags): TControl;
298 begin
299 if ihcScrollable in Flags then
300 Result:=TScrollableHTMLControl.Create(Owner)
301 else
302 Result:=TSimpleHTMLControl.Create(Owner);
303 if Provider=nil then
304 Provider:=CreateIDEHTMLProvider(Result);
305 if ihcScrollable in Flags then
306 begin
307 Provider.ControlIntf:=TScrollableHTMLControl(Result);
308 TScrollableHTMLControl(Result).Provider:=Provider;
309 end
310 else
311 begin
312 Provider.ControlIntf:=TSimpleHTMLControl(Result);
313 TSimpleHTMLControl(Result).Provider:=Provider;
314 end;
315 end;
316
317 // Default provider generator if no other is registered.
LazCreateIDEHTMLProvidernull318 function LazCreateIDEHTMLProvider(Owner: TComponent): TAbstractIDEHTMLProvider;
319 begin
320 Result:=TLazIDEHTMLProvider.Create(Owner);
321 TLazIDEHTMLProvider(Result).Providers:=TIDEHelpManager(HelpBoss).FHTMLProviders;
322 end;
323
CompareLIHProviderStreamnull324 function CompareLIHProviderStream(Data1, Data2: Pointer): integer;
325 begin
326 Result:=CompareStr(TLIHProviderStream(Data1).URL,TLIHProviderStream(Data2).URL);
327 end;
328
CompareURLWithLIHProviderStreamnull329 function CompareURLWithLIHProviderStream(URL, Stream: Pointer): integer;
330 begin
331 Result:=CompareStr(AnsiString(URL),TLIHProviderStream(Stream).URL);
332 end;
333
334 { TSimpleFPCKeywordHelpDatabase }
335
GetNodesForKeywordnull336 function TSimpleFPCKeywordHelpDatabase.GetNodesForKeyword(
337 const HelpKeyword: string; var ListOfNodes: THelpNodeQueryList;
338 var ErrMsg: string): TShowHelpResult;
339 var
340 KeyWord: String;
341 begin
342 Result:=shrHelpNotFound;
343 if (csDesigning in ComponentState) then exit;
344 if (FPCKeyWordHelpPrefix<>'')
345 and (LeftStr(HelpKeyword,length(FPCKeyWordHelpPrefix))=FPCKeyWordHelpPrefix) then begin
346 // HelpKeyword starts with KeywordPrefix
347 KeyWord:=copy(HelpKeyword,length(FPCKeyWordHelpPrefix)+1,length(HelpKeyword));
348 // test: testfcpkeyword
349 if KeyWord='testfcpkeyword' then begin
350 // this help database knows this keyword
351 // => add a node, so that if there are several possibilities the IDE can
352 // show the user a dialog to choose
353 if FKeywordPrefixNode=nil then
354 FKeywordPrefixNode:=THelpNode.CreateURL(Self,'','');
355 FKeywordPrefixNode.Title:='Pascal keyword '+KeyWord;
356 CreateNodeQueryListAndAdd(FKeywordPrefixNode,nil,ListOfNodes,true);
357 Result:=shrSuccess;
358 end;
359 end;
360 end;
361
TSimpleFPCKeywordHelpDatabase.ShowHelpnull362 function TSimpleFPCKeywordHelpDatabase.ShowHelp(Query: THelpQuery; BaseNode,
363 NewNode: THelpNode; QueryItem: THelpQueryItem; var ErrMsg: string
364 ): TShowHelpResult;
365 var
366 KeywordQuery: THelpQueryKeyword;
367 KeyWord: String;
368 begin
369 Result:=shrHelpNotFound;
370 if not (Query is THelpQueryKeyword) then exit;
371 KeywordQuery:=THelpQueryKeyword(Query);
372 KeyWord:=copy(KeywordQuery.Keyword,length(FPCKeyWordHelpPrefix)+1,length(KeywordQuery.Keyword));
373 debugln(['TSimpleFPCKeywordHelpDatabase.ShowHelp Keyword=',Keyword]);
374 // ToDo: implement me
375 ErrMsg:='';
376 end;
377
378 { TSimpleHTMLControl }
379
380 procedure TSimpleHTMLControl.SetProvider(const AValue: TAbstractIDEHTMLProvider);
381 begin
382 if FProvider=AValue then exit;
383 FProvider:=AValue;
384 end;
385
386 constructor TSimpleHTMLControl.Create(AOwner: TComponent);
387 begin
388 inherited Create(AOwner);
389 MaxLineCount:=30;
390 WordWrap := True;
391 Layout := tlCenter;
392 Alignment := taLeftJustify;
393 Font.Color := clInfoText;
394 BorderSpacing.Around := 4;
395 ShowAccelChar := False; //don't underline after &
396 end;
397
GetURLnull398 function TSimpleHTMLControl.GetURL: string;
399 begin
400 Result:=FURL;
401 end;
402
403 procedure TSimpleHTMLControl.SetURL(const AValue: string);
404 var
405 Stream: TStream;
406 Renderer: THTML2TextRenderer;
407 NewURL: String;
408 begin
409 if Provider=nil then raise Exception.Create('TSimpleHTMLControl.SetURL missing Provider');
410 if FURL=AValue then exit;
411 NewURL:=Provider.MakeURLAbsolute(Provider.BaseURL,AValue);
412 if FURL=NewURL then exit;
413 FURL:=NewURL;
414 try
415 Stream:=Provider.GetStream(FURL,true);
416 Renderer:=THTML2TextRenderer.Create(Stream);
417 try
418 Caption:=Renderer.Render(MaxLineCount);
419 finally
420 Renderer.Free;
421 Provider.ReleaseStream(FURL);
422 end;
423 except
424 on E: Exception do begin
425 Caption:=E.Message;
426 end;
427 end;
428 end;
429
430 procedure TSimpleHTMLControl.SetHTMLContent(Stream: TStream; const NewURL: string);
431 var
432 Renderer: THTML2TextRenderer;
433 begin
434 FURL:=NewURL;
435 Renderer:=THTML2TextRenderer.Create(Stream);
436 try
437 Caption:=Renderer.Render(MaxLineCount);
438 finally
439 Renderer.Free;
440 end;
441 //debugln(['TSimpleHTMLControl.SetHTMLContent: ',Caption]);
442 end;
443
444 procedure TSimpleHTMLControl.GetPreferredControlSize(out AWidth, AHeight: integer);
445 var
446 DC: HDC;
447 R: TRect;
448 OldFont: HGDIOBJ;
449 Flags: Cardinal;
450 LabelText: String;
451 begin
452 AWidth:=0;
453 AHeight:=0;
454 DC := GetDC(Parent.Handle);
455 try
456 R := Rect(0, 0, 600, 200);
457 OldFont := SelectObject(DC, HGDIOBJ(Font.Reference.Handle));
458 Flags := DT_CALCRECT or DT_EXPANDTABS;
459 inc(Flags, DT_WordBreak);
460 LabelText := GetLabelText;
461 DrawText(DC, PChar(LabelText), Length(LabelText), R, Flags);
462 SelectObject(DC, OldFont);
463 AWidth := R.Right - R.Left + 8; // border
464 AHeight := R.Bottom - R.Top + 8; // border
465 finally
466 ReleaseDC(Parent.Handle, DC);
467 end;
468 //DebugLn(['TSimpleHTMLControl.GetPreferredControlSize Caption="',Caption,'" ',AWidth,'x',AHeight]);
469 end;
470
471 { TScrollableHTMLControl }
472
473 procedure TScrollableHTMLControl.SetProvider(const AValue: TAbstractIDEHTMLProvider);
474 begin
475 if FProvider=AValue then exit;
476 FProvider:=AValue;
477 end;
478
479 constructor TScrollableHTMLControl.Create(AOwner: TComponent);
480 begin
481 inherited Create(AOwner);
482 BorderSpacing.Around := 4;
483 BorderStyle := bsNone;
484 ReadOnly := True;
485 ScrollBars := ssAutoVertical;
486 end;
487
GetURLnull488 function TScrollableHTMLControl.GetURL: string;
489 begin
490 Result:=FURL;
491 end;
492
493 procedure TScrollableHTMLControl.SetURL(const AValue: string);
494 var
495 Stream: TStream;
496 Renderer: THTML2TextRenderer;
497 NewURL: String;
498 begin
499 if Provider=nil then raise Exception.Create('TScrollableHTMLControl.SetURL missing Provider');
500 if FURL=AValue then exit;
501 NewURL:=Provider.MakeURLAbsolute(Provider.BaseURL,AValue);
502 if FURL=NewURL then exit;
503 FURL:=NewURL;
504 try
505 Stream:=Provider.GetStream(FURL,true);
506 Renderer:=THTML2TextRenderer.Create(Stream);
507 try
508 Caption:=Renderer.Render;
509 finally
510 Renderer.Free;
511 Provider.ReleaseStream(FURL);
512 end;
513 except
514 on E: Exception do begin
515 Caption:=E.Message;
516 end;
517 end;
518 end;
519
520 procedure TScrollableHTMLControl.SetHTMLContent(Stream: TStream; const NewURL: string);
521 var
522 Renderer: THTML2TextRenderer;
523 begin
524 FURL:=NewURL;
525 Renderer:=THTML2TextRenderer.Create(Stream);
526 try
527 Caption:=Renderer.Render;
528 finally
529 Renderer.Free;
530 end;
531 //debugln(['TScrollableHTMLControl.SetHTMLContent: ',Caption]);
532 end;
533
534 procedure TScrollableHTMLControl.GetPreferredControlSize(out AWidth, AHeight: integer);
535 begin
536 AWidth:=0;
537 AHeight:=0;
538 GetPreferredSize(AWidth, AHeight);
539 end;
540
541 { TLazIDEHTMLProvider }
542
543 procedure TLazIDEHTMLProvider.SetProviders(const AValue: TLIHProviders);
544 begin
545 if FProviders=AValue then exit;
546 FProviders:=AValue;
547 end;
548
549 procedure TLazIDEHTMLProvider.OpenNextURL(Data: PtrInt);
550 var
551 URLScheme: string;
552 URLPath: string;
553 URLParams: string;
554 AFilename: String;
555 p: TPoint;
556 begin
557 fWaitingForAsync:=false;
558 SplitURL(NextURL,URLScheme,URLPath,URLParams);
559 debugln(['TLazIDEHTMLProvider.OpenNextURL "',URLScheme,'" :// "',URLPath,'" & "',URLParams,'"']);
560 if URLScheme='source' then begin
561 p:=Point(1,1);
562 if REMatches(URLPath,'(.*)\((.*),(.*)\)') then begin
563 AFilename:=REVar(1);
564 p.Y:=StrToIntDef(REVar(2),p.x);
565 p.X:=StrToIntDef(REVar(3),p.y);
566 end else begin
567 AFilename:=URLPath;
568 end;
569 AFilename:=GetForcedPathDelims(AFilename);
570 LazarusIDE.DoOpenFileAndJumpToPos(AFilename,p,-1,-1,-1,[]);
571 end else if (URLScheme='openpackage') and IsValidIdent(URLPath) then begin
572 PackageEditingInterface.DoOpenPackageWithName(URLPath,[],false);
573 end else if (URLScheme='fpdoc') and (URLParams<>'') then begin
574 OpenFPDoc(URLParams);
575 end;
576 end;
577
578 procedure TLazIDEHTMLProvider.OpenFPDoc(Path: string);
579 var
580 RestPath: string;
581
582 function ExtractSubPath: string;
583 var
584 p: SizeInt;
585 begin
586 p:=System.Pos('.',RestPath);
587 if p<1 then p:=length(RestPath)+1;
588 Result:=copy(RestPath,1,p-1);
589 RestPath:=copy(RestPath,p+1,length(RestPath));
590 end;
591
592 procedure InvalidPathError(Msg: string);
593 begin
594 debugln(['InvalidPathError Path="',Path,'" Msg="',Msg,'"']);
595 IDEMessageDialog('Unable to open fpdoc help',
596 'The fpdoc path "'+Path+'" is invalid.'+LineEnding+Msg,
597 mtError,[mbCancel]);
598 end;
599
600 var
601 PkgName: String;
602 Pkg: TLazPackage;
603 AnUnitName: String;
604 PkgFile: TPkgFile;
605 ContextList: TPascalHelpContextList;
606 ElementName: String;
607 Filename: String;
608 ErrMsg: string;
609 PascalHelpContextLists: TList;
610 i: Integer;
611 PkgList: TFPList;
612 SubPkg: TLazPackage;
613 begin
614 RestPath:=Path;
615 PkgName:=ExtractSubPath;
616 if (PkgName='') or (PkgName[1]<>'#') then begin
617 InvalidPathError('It does not start with a package name, for example #rtl.');
618 exit;
619 end;
620 PkgName:=copy(PkgName,2,length(PkgName));
621 if not IsValidIdent(PkgName) then begin
622 InvalidPathError('It does not start with a package name, for example #rtl.');
623 exit;
624 end;
625 if SysUtils.CompareText(PkgName,'rtl')=0 then PkgName:='fcl';
626 Pkg:=TLazPackage(PackageEditingInterface.FindPackageWithName(PkgName));
627 if Pkg=nil then begin
628 InvalidPathError('Package "'+PkgName+'" not found.');
629 exit;
630 end;
631 if Pkg.IsVirtual then begin
632 InvalidPathError('Package "'+PkgName+'" has no help.');
633 exit;
634 end;
635
636 AnUnitName:=ExtractSubPath;
637 if not IsValidIdent(AnUnitName) then begin
638 InvalidPathError('Unit name "'+AnUnitName+'" is invalid.');
639 exit;
640 end;
641
642 Filename:='';
643 PkgFile:=Pkg.FindUnit(AnUnitName);
644 if PkgFile=nil then begin
645 // search in all sub packages
646 PkgList:=nil;
647 try
648 PackageGraph.GetAllRequiredPackages(nil,Pkg.FirstRequiredDependency,
649 PkgList);
650 if PkgList<>nil then begin
651 for i:=0 to PkgList.Count-1 do begin
652 SubPkg:=TLazPackage(PkgList[i]);
653 PkgFile:=SubPkg.FindUnit(AnUnitName);
654 if PkgFile<>nil then begin
655 Pkg:=SubPkg;
656 break;
657 end;
658 end;
659 end;
660 finally
661 PkgList.Free;
662 end;
663 end;
664 if (PkgFile<>nil) and (PkgFile.FileType in PkgFileRealUnitTypes) then begin
665 // normal unit in lpk
666 Filename:=PkgFile.GetFullFilename;
667 end else if SysUtils.CompareText(PkgName,'fcl')=0 then begin
668 // search in FPC sources
669 Filename:=CodeToolBoss.DirectoryCachePool.FindUnitInUnitSet('',AnUnitName);
670 end;
671 if Filename='' then begin
672 InvalidPathError('Unit "'+AnUnitName+'" was not found in package '+Pkg.Name+'.');
673 exit;
674 end;
675
676 PascalHelpContextLists:=TList.Create;
677 try
678 // create a context list (and add it as sole element to the PascalHelpContextLists)
679 ContextList:=TPascalHelpContextList.Create;
680 PascalHelpContextLists.Add(ContextList);
681 ContextList.Add(pihcFilename,Filename);
682 ContextList.Add(pihcSourceName,AnUnitName);
683 repeat
684 ElementName:=ExtractSubPath;
685 if ElementName='' then break;
686 ContextList.Add(pihcType,ElementName);
687 until false;
688 ErrMsg:='TLazIDEHTMLProvider.OpenFPDoc ShowHelpForPascalContexts';
689 ShowHelpForPascalContexts(Filename,Point(1,1),PascalHelpContextLists,ErrMsg);
690 finally
691 if PascalHelpContextLists<>nil then begin
692 for i:=0 to PascalHelpContextLists.Count-1 do
693 TObject(PascalHelpContextLists[i]).Free;
694 PascalHelpContextLists.Free;
695 end;
696 end;
697 end;
698
699 destructor TLazIDEHTMLProvider.Destroy;
700 begin
701 if (Application<>nil) and fWaitingForAsync then
702 Application.RemoveAsyncCalls(Self);
703 inherited Destroy;
704 end;
705
URLHasStreamnull706 function TLazIDEHTMLProvider.URLHasStream(const URL: string): boolean;
707 var
708 URLScheme: string;
709 URLPath: string;
710 URLParams: string;
711 begin
712 Result:=false;
713 SplitURL(URL,URLScheme,URLPath,URLParams);
714 if (URLScheme='file') or (URLScheme='lazdoc') or (URLScheme='fpdoc') then
715 Result:=true;
716 end;
717
718 procedure TLazIDEHTMLProvider.OpenURLAsync(const URL: string);
719 begin
720 NextURL:=URL;
721 //debugln(['TLazIDEHTMLProvider.OpenURLAsync URL=',URL]);
722 if not fWaitingForAsync then begin
723 Application.QueueAsyncCall(@OpenNextURL,0);
724 fWaitingForAsync:=true;
725 end;
726 end;
727
GetStreamnull728 function TLazIDEHTMLProvider.GetStream(const URL: string; Shared: Boolean): TStream;
729 begin
730 Result:=FProviders.GetStream(URL,Shared);
731 end;
732
733 procedure TLazIDEHTMLProvider.ReleaseStream(const URL: string);
734 begin
735 FProviders.ReleaseStream(URL);
736 end;
737
738 { TLIHProviders }
739
740 constructor TLIHProviders.Create;
741 begin
742 FStreams:=TAVLTree.Create(@CompareLIHProviderStream);
743 end;
744
745 destructor TLIHProviders.Destroy;
746 begin
747 FStreams.FreeAndClear;
748 FreeAndNil(FStreams);
749 inherited Destroy;
750 end;
751
FindStreamnull752 function TLIHProviders.FindStream(const URL: string; CreateIfNotExists: Boolean
753 ): TLIHProviderStream;
754 var
755 Node: TAVLTreeNode;
756 begin
757 if URL='' then
758 exit(nil);
759 Node:=FStreams.FindKey(Pointer(URL),@CompareURLWithLIHProviderStream);
760 if Node<>nil then begin
761 Result:=TLIHProviderStream(Node.Data);
762 end else if CreateIfNotExists then begin
763 Result:=TLIHProviderStream.Create;
764 Result.URL:=URL;
765 FStreams.Add(Result);
766 end else
767 Result:=nil;
768 end;
769
GetStreamnull770 function TLIHProviders.GetStream(const URL: string; Shared: boolean): TStream;
771
772 procedure OpenFile(out Stream: TStream; const Filename: string;
773 UseCTCache: boolean);
774 var
775 fs: TFileStream;
776 ok: Boolean;
777 Buf: TCodeBuffer;
778 ms: TMemoryStream;
779 begin
780 if UseCTCache then begin
781 Buf:=CodeToolBoss.LoadFile(Filename,true,false);
782 if Buf=nil then
783 raise Exception.Create('TLIHProviders.GetStream: unable to open file '+Filename);
784 ms:=TMemoryStream.Create;
785 Buf.SaveToStream(ms);
786 ms.Position:=0;
787 Result:=ms;
788 end else begin
789 fs:=nil;
790 ok:=false;
791 try
792 DebugLn(['TLIHProviders.GetStream.OpenFile ',Filename]);
793 fs:=TFileStream.Create(Filename,fmOpenRead);
794 Stream:=fs;
795 ok:=true;
796 finally
797 if not ok then
798 fs.Free;
799 end;
800 end;
801 end;
802
803
804 {const
805 HTML =
806 '<HTML>'+#10
807 +'<BODY>'+#10
808 +'Test'+#10
809 +'</BODY>'+#10
810 +'</HTML>';}
811 var
812 Stream: TLIHProviderStream;
813 URLType: string;
814 URLPath: string;
815 URLParams: string;
816 begin
817 if URL='' then raise Exception.Create('TLIHProviders.GetStream no URL');
818 if Shared then begin
819 Stream:=FindStream(URL,true);
820 Stream.IncreaseRefCount;
821 Result:=Stream.Stream;
822 end else begin
823 Stream:=nil;
824 Result:=nil;
825 end;
826 try
827 if Result=nil then begin
828 SplitURL(URL,URLType,URLPath,URLParams);
829 {$ifdef VerboseLazDoc}
830 DebugLn(['TLIHProviders.GetStream URLType=',URLType,' URLPath=',URLPath,' URLParams=',URLParams]);
831 {$endif}
832 if URLType='lazdoc' then begin
833 if copy(URLPath,1,8)='lazarus/' then begin
834 URLPath:=copy(URLPath,9,length(URLPath));
835 if (URLPath='index.html')
836 or (URLPath='images/laztitle.jpg')
837 or (URLPath='images/cheetah1.png')
838 or (URLPath='lazdoc.css')
839 then begin
840 OpenFile(Result,
841 EnvironmentOptions.GetParsedLazarusDirectory
842 +GetForcedPathDelims('/docs/'+URLPath),
843 true);
844 end;
845 end;
846 end else if URLType='file' then begin
847 OpenFile(Result,GetForcedPathDelims(URLPath),true);
848 end;
849 {Result:=TMemoryStream.Create;
850 Stream.Stream:=Result;
851 Result.Write(HTML[1],length(HTML));
852 Result.Position:=0;}
853 if Result=nil then
854 raise Exception.Create('TLIHProviders.GetStream: URL not found "'+dbgstr(URL)+'"');
855 if Stream<>nil then
856 Stream.Stream:=Result;
857 end;
858 finally
859 if (Result=nil) and (Stream<>nil) then
860 ReleaseStream(URL);
861 end;
862 end;
863
864 procedure TLIHProviders.ReleaseStream(const URL: string);
865 var
866 Stream: TLIHProviderStream;
867 begin
868 Stream:=FindStream(URL,false);
869 if Stream=nil then
870 raise Exception.Create('TLIHProviders.ReleaseStream "'+URL+'"');
871 Stream.DecreaseRefCount;
872 if Stream.RefCount=0 then begin
873 FStreams.Remove(Stream);
874 Stream.Free;
875 end;
876 end;
877
878 { TLIHProviderStream }
879
880 destructor TLIHProviderStream.Destroy;
881 begin
882 FreeAndNil(Stream);
883 inherited Destroy;
884 end;
885
886 procedure TLIHProviderStream.IncreaseRefCount;
887 begin
888 inc(FRefCount);
889 end;
890
891 procedure TLIHProviderStream.DecreaseRefCount;
892 begin
893 if FRefCount<=0 then
894 raise Exception.Create('TLIHProviderStream.DecreaseRefCount');
895 dec(FRefCount);
896 end;
897
898 { THelpSelectorDialog }
899
900 procedure THelpSelectorDialog.HelpSelectorDialogClose(Sender: TObject;
901 var CloseAction: TCloseAction);
902 begin
903 IDEDialogLayoutList.SaveLayout(Self);
904 end;
905
906 procedure THelpSelectorDialog.NodesTreeViewDblClick(Sender: TObject);
907 begin
908 ModalResult := mrOK;
909 end;
910
911 procedure THelpSelectorDialog.NodesTreeViewSelectionChanged(Sender: TObject);
912 begin
913 UpdateButtons;
914 end;
915
916 procedure THelpSelectorDialog.SetNodes(const AValue: THelpNodeQueryList);
917 begin
918 if FNodes=AValue then exit;
919 FNodes:=AValue;
920 FillNodesTV;
921 end;
922
923 procedure THelpSelectorDialog.FillNodesTV;
924 var
925 i: Integer;
926 NodeQuery: THelpNodeQuery;
927 Node: THelpNode;
928 DB: THelpDatabase;
929 DBTVNode, TVNode: TTreeNode;
930 begin
931 NodesTreeView.BeginUpdate;
932 try
933 TVNode:=nil;
934 NodesTreeView.Items.Clear;
935 if (Nodes<>nil) then begin
936 for i:=0 to Nodes.Count-1 do begin
937 NodeQuery:=Nodes[i];
938 Node:=NodeQuery.Node;
939 DB:=Node.Owner;
940
941 DBTVNode:=NodesTreeView.Items.FindTopLvlNode(DB.ID);
942 if DBTVNode=nil then
943 begin
944 DBTVNode:=NodesTreeView.Items.AddChild(nil,DB.ID);
945 DBTVNode.ImageIndex:=FImgIndexDB;
946 DBTVNode.SelectedIndex:=FImgIndexDB;
947 end;
948
949 TVNode:=NodesTreeView.Items.AddChild(DBTVNode,NodeQuery.AsString);
950 TVNode.ImageIndex:=FImgIndexNode;
951 TVNode.SelectedIndex:=FImgIndexNode;
952 TVNode.Data:=NodeQuery;
953
954 DBTVNode.Expand(true);
955 end;
956 end;
957 NodesTreeView.Selected:=TVNode;
958 finally
959 NodesTreeView.EndUpdate;
960 end;
961 end;
962
963 procedure THelpSelectorDialog.UpdateButtons;
964 begin
965 BtnPanel.OKButton.Enabled:=GetSelectedNodeQuery<>nil;
966 end;
967
968 constructor THelpSelectorDialog.Create(TheOwner: TComponent);
969 begin
970 inherited Create(TheOwner);
971 IDEDialogLayoutList.ApplyLayout(Self,500,300);
972
973 Caption := lisHelpSelectorDialog;
974 NodesGroupBox.Caption:=lisSelectAHelpItem;
975 BtnPanel.OKButton.Caption:=lisMenuOk;
976
977 NodesTreeView.Images:=IDEImages.Images_16;
978 FImgIndexDB:=IDEImages.LoadImage('item_package');
979 FImgIndexNode:=IDEImages.LoadImage('btn_help');
980 end;
981
GetSelectedNodeQuerynull982 function THelpSelectorDialog.GetSelectedNodeQuery: THelpNodeQuery;
983 var
984 TVNode: TTreeNode;
985 begin
986 Result:=nil;
987 TVNode:=NodesTreeView.Selected;
988 if (TVNode=nil) or (TVNode.Data=nil) then exit;
989 Result:=TObject(TVNode.Data) as THelpNodeQuery;
990 end;
991
992 { TIDEHelpDatabases }
993
ShowHelpSelectornull994 function TIDEHelpDatabases.ShowHelpSelector(Query: THelpQuery;
995 Nodes: THelpNodeQueryList;
996 var ErrMsg: string;
997 var Selection: THelpNodeQuery
998 ): TShowHelpResult;
999 var
1000 Dialog: THelpSelectorDialog;
1001 begin
1002 Selection:=nil;
1003 Result:=shrNone;
1004 Dialog:=THelpSelectorDialog.Create(nil);
1005 try
1006 Dialog.Nodes:=Nodes;
1007 if Dialog.ShowModal=mrOk then begin
1008 Selection:=Dialog.GetSelectedNodeQuery;
1009 if Selection<>nil then
1010 Result:=shrSuccess;
1011 end else begin
1012 Result:=shrCancel;
1013 end;
1014 finally
1015 Dialog.Free;
1016 end;
1017 end;
1018
GetBaseDirectoryForBasePathObjectnull1019 function TIDEHelpDatabases.GetBaseDirectoryForBasePathObject(
1020 BasePathObject: TObject): string;
1021 var
1022 s: String;
1023 begin
1024 Result:='';
1025 DebugLn('TIDEHelpDatabases.GetBaseDirectoryForBasePathObject BasePathObject=',dbgsName(BasePathObject));
1026 if (BasePathObject is THelpBasePathObject) then
1027 Result:=THelpBasePathObject(BasePathObject).BasePath
1028 else if (BasePathObject=HelpBoss) or (BasePathObject=MainIDEInterface) then
1029 Result:=EnvironmentOptions.GetParsedLazarusDirectory
1030 else if BasePathObject is TProject then
1031 Result:=TProject(BasePathObject).Directory
1032 else if BasePathObject is TLazPackage then
1033 Result:=TLazPackage(BasePathObject).Directory;
1034 if Result<>'' then begin
1035 s:=Result;
1036 if not IDEMacros.SubstituteMacros(Result) then
1037 debugln(['TIDEHelpDatabases.GetBaseDirectoryForBasePathObject macros failed "',s,'"']);
1038 end;
1039 Result:=AppendPathDelim(Result);
1040 end;
1041
ShowHelpForSourcePositionnull1042 function TIDEHelpDatabases.ShowHelpForSourcePosition(
1043 Query: THelpQuerySourcePosition; var ErrMsg: string): TShowHelpResult;
1044 begin
1045 Result:=HelpBoss.ShowHelpForSourcePosition(Query.Filename,
1046 Query.SourcePosition,ErrMsg);
1047 end;
1048
SubstituteMacrosnull1049 function TIDEHelpDatabases.SubstituteMacros(var s: string): boolean;
1050 begin
1051 Result:=IDEMacros.SubstituteMacros(s);
1052 end;
1053
1054 { TIDEHelpManager }
1055
1056 procedure TIDEHelpManager.mnuSearchInFPDocFilesClick(Sender: TObject);
1057 begin
1058 ShowFPDocFileSearch;
1059 end;
1060
1061 procedure TIDEHelpManager.mnuEditMessageHelpClick(Sender: TObject);
1062 begin
1063
1064 end;
1065
1066 procedure TIDEHelpManager.mnuHelpAboutLazarusClicked(Sender: TObject);
1067 begin
1068 ShowAboutForm;
1069 end;
1070
1071 procedure TIDEHelpManager.mnuHelpOnlineHelpClicked(Sender: TObject);
1072 begin
1073 ShowLazarusHelpStartPage;
1074 end;
1075
1076 procedure TIDEHelpManager.mnuHelpReportBugClicked(Sender: TObject);
1077 begin
1078 OpenURL(lisReportingBugURL);
1079 end;
1080
1081 procedure TIDEHelpManager.RegisterIDEHelpDatabases;
1082
1083 procedure CreateMainIDEHelpDB;
1084 var
1085 StartNode: THelpNode;
1086 HTMLHelp: THTMLHelpDatabase;
1087 begin
1088 FMainHelpDB:=HelpDatabases.CreateHelpDatabase(lihcStartPage,
1089 THTMLHelpDatabase,true);
1090 HTMLHelp:=FMainHelpDB as THTMLHelpDatabase;
1091 FMainHelpDBPath:=THelpBasePathObject.Create('$(LazarusDir)/docs');
1092 HTMLHelp.BasePathObject:=FMainHelpDBPath;
1093
1094 // HTML nodes for the IDE
1095 StartNode:=THelpNode.CreateURLID(HTMLHelp,'Lazarus',
1096 'file://index.html',lihcStartPage);
1097 HTMLHelp.TOCNode:=THelpNode.Create(HTMLHelp,StartNode);// once as TOC
1098 HTMLHelp.RegisterItemWithNode(StartNode);// and once as normal page
1099 end;
1100
1101 procedure CreateRTLHelpDB;
1102 var
1103 HTMLHelp: TFPDocHTMLHelpDatabase;
1104 FPDocNode: THelpNode;
1105 DirItem: THelpDBISourceDirectory;
1106 begin
1107 FRTLHelpDB:=HelpDatabases.CreateHelpDatabase(lihcRTLUnits,
1108 TFPDocHTMLHelpDatabase,true);
1109 HTMLHelp:=FRTLHelpDB as TFPDocHTMLHelpDatabase;
1110 HTMLHelp.DefaultBaseURL:=lihRTLURL;
1111 FRTLHelpDBPath:=THelpBaseURLObject.Create;
1112 HTMLHelp.BasePathObject:=FRTLHelpDBPath;
1113
1114 // FPDoc nodes for units in the RTL
1115 FPDocNode:=THelpNode.CreateURL(HTMLHelp,
1116 'RTL - Free Pascal Run Time Library Units',
1117 'file://index.html');
1118 HTMLHelp.TOCNode:=THelpNode.Create(HTMLHelp,FPDocNode);// once as TOC
1119 DirItem:=THelpDBISourceDirectories.Create(FPDocNode,'$(FPCSrcDir)',
1120 'rtl;packages/rtl-console/src;packages/rtl-extra/src;packages/rtl-objpas/src;packages/rtl-unicode/src',
1121 '*.pp;*.pas',true);// and once as normal page
1122 HTMLHelp.RegisterItem(DirItem);
1123 end;
1124
1125 procedure CreateFCLHelpDB;
1126 var
1127 HTMLHelp: TFPDocHTMLHelpDatabase;
1128 FPDocNode: THelpNode;
1129 DirItem: THelpDBISourceDirectory;
1130 begin
1131 FFCLHelpDB:=HelpDatabases.CreateHelpDatabase(lihcFCLUnits,
1132 TFPDocHTMLHelpDatabase,true);
1133 HTMLHelp:=FFCLHelpDB as TFPDocHTMLHelpDatabase;
1134 HTMLHelp.DefaultBaseURL:=lihFCLURL;
1135 FFCLHelpDBPath:=THelpBaseURLObject.Create;
1136 HTMLHelp.BasePathObject:=FFCLHelpDBPath;
1137
1138 // FPDoc nodes for units in the FCL
1139 // create TOC
1140 HTMLHelp.TOCNode:=THelpNode.CreateURL(HTMLHelp,
1141 'FCL - Free Pascal Component Library Units',
1142 'file://index.html');
1143
1144 // FPC 2.0.x FCL source directory
1145 FPDocNode:=THelpNode.CreateURL(HTMLHelp,
1146 'FCL - Free Pascal Component Library Units (2.0.x)',
1147 'file://index.html');
1148 DirItem:=THelpDBISourceDirectory.Create(FPDocNode,
1149 '$(FPCSrcDir)/fcl/inc','*.pp;*.pas',false);
1150 HTMLHelp.RegisterItem(DirItem);
1151
1152 // FPC 2.2.x FCL source directory
1153 FPDocNode:=THelpNode.CreateURL(HTMLHelp,
1154 'FCL - Free Pascal Component Library Units',
1155 'file://index.html');
1156 DirItem:=THelpDBISourceDirectory.Create(FPDocNode,
1157 '$(FPCSrcDir)/packages/fcl-base/src','*.pp;*.pas',true);
1158 HTMLHelp.RegisterItem(DirItem);
1159
1160 // FPC 2.4.4+ FCL source directory
1161 FPDocNode:=THelpNode.CreateURL(HTMLHelp,
1162 'FCL - Free Pascal Component Library Units',
1163 'file://index.html');
1164 DirItem:=THelpDBISourceDirectories.Create(FPDocNode,'$(FPCSrcDir)/packages',
1165 'fcl-base/src;fcl-db/src;fcl-extra/src;fcl-process/src;fcl-web/src;paszlib/src',
1166 '*.pp;*.pas',true);
1167 HTMLHelp.RegisterItem(DirItem);
1168 end;
1169
1170 procedure CreateLCLHelpDB;
1171 var
1172 HTMLHelp: TFPDocHTMLHelpDatabase;
1173 FPDocNode: THelpNode;
1174 DirItem: THelpDBISourceDirectory;
1175 begin
1176 FLCLHelpDB:=HelpDatabases.CreateHelpDatabase(lihcLCLUnits,
1177 TFPDocHTMLHelpDatabase,true);
1178 HTMLHelp:=FLCLHelpDB as TFPDocHTMLHelpDatabase;
1179 HTMLHelp.DefaultBaseURL:=lihLCLURL;
1180 FLCLHelpDBPath:=THelpBaseURLObject.Create;
1181 HTMLHelp.BasePathObject:=FLCLHelpDBPath;
1182
1183 // FPDoc nodes for units in the LCL
1184 FPDocNode:=THelpNode.CreateURL(HTMLHelp,
1185 'LCL - Lazarus Component Library Units',
1186 'file://index.html');
1187 HTMLHelp.TOCNode:=THelpNode.Create(HTMLHelp,FPDocNode);// once as TOC
1188 DirItem:=THelpDBISourceDirectory.Create(FPDocNode,'$(LazarusDir)/lcl',
1189 '*.pp;*.pas',true);// and once as normal page
1190 HTMLHelp.RegisterItem(DirItem);
1191 end;
1192
1193 procedure CreateLazUtilsHelpDB;
1194 var
1195 HTMLHelp: TFPDocHTMLHelpDatabase;
1196 FPDocNode: THelpNode;
1197 DirItem: THelpDBISourceDirectory;
1198 begin
1199 FLazUtilsHelpDB:=HelpDatabases.CreateHelpDatabase(lihcLazUtilsUnits,
1200 TFPDocHTMLHelpDatabase,true);
1201 HTMLHelp:=FLazUtilsHelpDB as TFPDocHTMLHelpDatabase;
1202 HTMLHelp.DefaultBaseURL:=lihLazUtilsURL;
1203 FLazUtilsHelpDBPath:=THelpBaseURLObject.Create;
1204 HTMLHelp.BasePathObject:=FLazUtilsHelpDBPath;
1205
1206 // FPDoc nodes for units in the LazUtils
1207 FPDocNode:=THelpNode.CreateURL(HTMLHelp,
1208 'LazUtils - Lazarus Utilities Library Units',
1209 'file://index.html');
1210 HTMLHelp.TOCNode:=THelpNode.Create(HTMLHelp,FPDocNode);// once as TOC
1211 DirItem:=THelpDBISourceDirectory.Create(FPDocNode,
1212 '$(LazarusDir)/components/lazutils',
1213 '*.pp;*.pas',true);// and once as normal page
1214 HTMLHelp.RegisterItem(DirItem);
1215 end;
1216
1217 procedure CreateFPCKeywordsHelpDB;
1218 begin
1219 {$IFDEF EnableSimpleFPCKeyWordHelpDB}
1220 HelpDatabases.CreateHelpDatabase('SimpleDemoForFPCKeyWordHelpDB',
1221 TSimpleFPCKeywordHelpDatabase,true);
1222 {$ENDIF}
1223 end;
1224
1225 begin
1226 CreateMainIDEHelpDB;
1227 CreateRTLHelpDB;
1228 CreateFCLHelpDB;
1229 CreateLCLHelpDB;
1230 CreateFPCMessagesHelpDB;
1231 CreateFPCKeywordsHelpDB;
1232 CreateLazUtilsHelpDB;
1233 end;
1234
1235 procedure TIDEHelpManager.RegisterDefaultIDEHelpViewers;
1236 var
1237 HelpViewer: THTMLBrowserHelpViewer;
1238 begin
1239 HelpViewer:= THTMLBrowserHelpViewer.Create(nil);
1240 HelpViewer.OnFindDefaultBrowser := @FindDefaultBrowser;
1241 HelpViewers.RegisterViewer(HelpViewer);
1242 end;
1243
1244 procedure TIDEHelpManager.FindDefaultBrowser(var DefaultBrowser, Params: string);
1245 begin
1246 GetDefaultBrowser(DefaultBrowser, Params);
1247 end;
1248
CollectKeywordsnull1249 function TIDEHelpManager.CollectKeywords(CodeBuffer: TCodeBuffer;
1250 const CodePos: TPoint; out Identifier: string): TShowHelpResult;
1251 // Collect keywords and show help if possible
1252 var
1253 p: Integer;
1254 IdentStart, IdentEnd: integer;
1255 KeyWord: String;
1256 ErrorMsg: String;
1257 begin
1258 Result:=shrHelpNotFound;
1259 Identifier:='';
1260 p:=0;
1261 CodeBuffer.LineColToPosition(CodePos.Y,CodePos.X,p);
1262 if p<1 then exit;
1263 GetIdentStartEndAtPosition(CodeBuffer.Source,p,IdentStart,IdentEnd);
1264 if IdentEnd<=IdentStart then exit;
1265 Identifier:=copy(CodeBuffer.Source,IdentStart,IdentEnd-IdentStart);
1266 if (IdentStart > 1) and (CodeBuffer.Source[IdentStart - 1] in ['$','%']) then
1267 Dec(IdentStart);
1268 KeyWord:=copy(CodeBuffer.Source,IdentStart,IdentEnd-IdentStart);
1269 ErrorMsg:='';
1270 if KeyWord[1] = '$' then
1271 Result:=ShowHelpForDirective('',FPCDirectiveHelpPrefix+Keyword,ErrorMsg)
1272 else if KeyWord[1] = '%' then
1273 Result:=ShowHelpForDirective('',IDEDirectiveHelpPrefix+Keyword,ErrorMsg)
1274 else
1275 Result:=ShowHelpForKeyword('',FPCKeyWordHelpPrefix+Keyword,ErrorMsg);
1276 if Result=shrSuccess then
1277 exit;
1278 if Result in [shrNone,shrDatabaseNotFound,shrContextNotFound,shrHelpNotFound] then
1279 exit(shrHelpNotFound); // not an FPC keyword
1280 // viewer error
1281 HelpManager.ShowError(Result,ErrorMsg);
1282 Result:=shrCancel;
1283 end;
1284
CollectDeclarationsnull1285 function TIDEHelpManager.CollectDeclarations(CodeBuffer: TCodeBuffer;
1286 const CodePos: TPoint; out Complete: boolean; var ErrMsg: string
1287 ): TShowHelpResult;
1288 // Collect declarations and show help if possible
1289 var
1290 NewList: TPascalHelpContextList;
1291 PascalHelpContextLists: TList;
1292 ListOfPCodeXYPosition: TFPList;
1293 CurCodePos: PCodeXYPosition;
1294 i: Integer;
1295 Flags: TFindDeclarationListFlags;
1296 begin
1297 Complete:=false;
1298 Result:=shrHelpNotFound;
1299 ListOfPCodeXYPosition:=nil;
1300 PascalHelpContextLists:=nil;
1301 try
1302 // get all possible declarations of this identifier
1303 debugln(['CollectDeclarations ',CodeBuffer.Filename,' line=',CodePos.Y,' col=',CodePos.X]);
1304 Flags:=[fdlfWithoutEmptyProperties,fdlfWithoutForwards];
1305 if CombineSameIdentifiersInUnit then
1306 Include(Flags,fdlfOneOverloadPerUnit);
1307 if CodeToolBoss.FindDeclarationAndOverload(CodeBuffer,CodePos.X,CodePos.Y,
1308 ListOfPCodeXYPosition,Flags)
1309 then begin
1310 if ListOfPCodeXYPosition=nil then exit;
1311 debugln('TIDEHelpManager.ShowHelpForSourcePosition Success, number of declarations: ',dbgs(ListOfPCodeXYPosition.Count));
1312 // convert the source positions in Pascal help context list
1313 for i:=0 to ListOfPCodeXYPosition.Count-1 do begin
1314 CurCodePos:=PCodeXYPosition(ListOfPCodeXYPosition[i]);
1315 debugln('TIDEHelpManager.ShowHelpForSourcePosition Declaration at ',dbgs(CurCodePos));
1316 NewList:=ConvertCodePosToPascalHelpContext(CurCodePos);
1317 if NewList<>nil then begin
1318 if PascalHelpContextLists=nil then
1319 PascalHelpContextLists:=TList.Create;
1320 PascalHelpContextLists.Add(NewList);
1321 end;
1322 end;
1323 if PascalHelpContextLists=nil then exit;
1324
1325 // invoke help system
1326 Complete:=true;
1327 debugln(['TIDEHelpManager.ShowHelpForSourcePosition PascalHelpContextLists.Count=',PascalHelpContextLists.Count,' calling ShowHelpForPascalContexts...']);
1328 Result:=ShowHelpForPascalContexts(CodeBuffer.Filename,CodePos,PascalHelpContextLists,ErrMsg);
1329 end else if CodeToolBoss.ErrorCode<>nil then begin
1330 MainIDEInterface.DoJumpToCodeToolBossError;
1331 Complete:=True;
1332 end;
1333 finally
1334 FreeListOfPCodeXYPosition(ListOfPCodeXYPosition);
1335 if PascalHelpContextLists<>nil then begin
1336 for i:=0 to PascalHelpContextLists.Count-1 do
1337 TObject(PascalHelpContextLists[i]).Free;
1338 PascalHelpContextLists.Free;
1339 end;
1340 end;
1341 end;
1342
1343 constructor TIDEHelpManager.Create(TheOwner: TComponent);
1344 begin
1345 inherited Create(TheOwner);
1346 HelpBoss:=Self;
1347 LazarusHelp:=Self;
1348 HelpOpts:=THelpOptions.Create;
1349 HelpOpts.SetDefaultFilename;
1350 HelpDatabases:=TIDEHelpDatabases.Create;
1351 HelpIntfs.HelpManager:=HelpDatabases;
1352 HelpViewers:=THelpViewers.Create;
1353 RegisterIDEHelpDatabases;
1354 RegisterDefaultIDEHelpViewers;
1355 CombineSameIdentifiersInUnit:=true;
1356 ShowCodeBrowserOnUnknownIdentifier:=true;
1357
1358 CodeHelpBoss:=TCodeHelpManager.Create(Self);
1359
1360 // register property editors for URL handling
1361 RegisterPropertyEditor(TypeInfo(AnsiString),
1362 THTMLHelpDatabase,'BaseURL',TURLDirectoryPropertyEditor);
1363
1364 FHTMLProviders:=TLIHProviders.Create;
1365
1366 if CreateIDEHTMLControl=nil then
1367 CreateIDEHTMLControl:=@LazCreateIDEHTMLControl;
1368 if CreateIDEHTMLProvider=nil then
1369 CreateIDEHTMLProvider:=@LazCreateIDEHTMLProvider;
1370 end;
1371
1372 destructor TIDEHelpManager.Destroy;
1373 begin
1374 FreeThenNil(FHTMLProviders);
1375 FreeThenNil(CodeHelpBoss);
1376 FPCMessagesHelpDB:=nil;
1377 FreeLCLHelpSystem;
1378 FreeThenNil(HelpOpts);
1379 FreeThenNil(FMainHelpDBPath);
1380 FreeThenNil(FRTLHelpDBPath);
1381 FreeThenNil(FFCLHelpDBPath);
1382 FreeThenNil(FLCLHelpDBPath);
1383 FreeThenNil(FLazUtilsHelpDBPath);
1384 HelpBoss:=nil;
1385 LazarusHelp:=nil;
1386 inherited Destroy;
1387 end;
1388
1389 procedure TIDEHelpManager.ConnectMainBarEvents;
1390 begin
1391 {$IFDEF Darwin}
1392 // ToDo: Place the "About Lazarus" under MacOS Application menu. See issue #12294.
1393 MainIDEBar.itmHelpAboutLazarus.OnClick := @mnuHelpAboutLazarusClicked;
1394 {$ELSE}
1395 MainIDEBar.itmHelpAboutLazarus.OnClick := @mnuHelpAboutLazarusClicked;
1396 {$ENDIF}
1397 MainIDEBar.itmHelpOnlineHelp.OnClick := @mnuHelpOnlineHelpClicked;
1398 MainIDEBar.itmHelpReportingBug.OnClick := @mnuHelpReportBugClicked;
1399
1400 {$IFDEF EnableFPDocSearch}
1401 MainIDEBar.itmSearchInFPDocFiles.OnClick:=@mnuSearchInFPDocFilesClick;
1402 {$ENDIF}
1403 end;
1404
1405 procedure TIDEHelpManager.LoadHelpOptions;
1406 begin
1407 HelpOpts.Load;
1408 end;
1409
1410 procedure TIDEHelpManager.SaveHelpOptions;
1411 begin
1412 HelpOpts.Save;
1413 end;
1414
1415 procedure TIDEHelpManager.ShowLazarusHelpStartPage;
1416 begin
1417 ShowIDEHelpForKeyword(lihcStartPage);
1418 end;
1419
1420 procedure TIDEHelpManager.ShowIDEHelpForContext(HelpContext: THelpContext);
1421 begin
1422 ShowHelpOrErrorForContext(MainHelpDB.ID,HelpContext);
1423 end;
1424
1425 procedure TIDEHelpManager.ShowIDEHelpForKeyword(const Keyword: string);
1426 begin
1427 ShowHelpOrErrorForKeyword(MainHelpDB.ID,Keyword);
1428 end;
1429
ShowHelpForSourcePositionnull1430 function TIDEHelpManager.ShowHelpForSourcePosition(const Filename: string;
1431 const CodePos: TPoint; var ErrMsg: string): TShowHelpResult;
1432 var
1433 CodeBuffer: TCodeBuffer;
1434 Complete: boolean;
1435 Identifier: string;
1436 begin
1437 debugln('TIDEHelpManager.ShowHelpForSourcePosition A Filename=',Filename,' ',dbgs(CodePos));
1438 Result:=shrHelpNotFound;
1439 ErrMsg:='No help found for "'+Filename+'"'
1440 +' at ('+IntToStr(CodePos.Y)+','+IntToStr(CodePos.X)+')';
1441 // commit editor changes
1442 if not CodeToolBoss.GatherExternalChanges then exit;
1443 // get code buffer for Filename
1444 if mrOk<>LoadCodeBuffer(CodeBuffer,FileName,[lbfCheckIfText],false) then
1445 exit;
1446
1447 Result:=CollectDeclarations(CodeBuffer,CodePos,Complete,ErrMsg);
1448 if Complete then exit;
1449
1450 debugln(['TIDEHelpManager.ShowHelpForSourcePosition no declaration found, trying keywords and built-in functions...']);
1451 Result:=CollectKeywords(CodeBuffer,CodePos,Identifier);
1452 if Result in [shrCancel,shrSuccess] then exit;
1453 if IsValidIdent(Identifier) and ShowCodeBrowserOnUnknownIdentifier then
1454 begin
1455 debugln(['TIDEHelpManager.ShowHelpForSourcePosition "',Identifier,'" is not an FPC keyword, search via code browser...']);
1456 ShowCodeBrowser(Identifier);
1457 exit(shrSuccess);
1458 end;
1459 debugln(['TIDEHelpManager.ShowHelpForSourcePosition "',Identifier,'" is not an FPC keyword']);
1460 end;
1461
ConvertCodePosToPascalHelpContextnull1462 function TIDEHelpManager.ConvertCodePosToPascalHelpContext(
1463 ACodePos: PCodeXYPosition): TPascalHelpContextList;
1464
1465 procedure AddContext(Descriptor: TPascalHelpContextType;
1466 const Context: string);
1467 begin
1468 Result.Add(Descriptor,Context);
1469 //debugln(' AddContext Descriptor=',dbgs(ord(Descriptor)),' Context="',Context,'"');
1470 end;
1471
1472 procedure AddContextsBackwards(Tool: TCodeTool;
1473 Node: TCodeTreeNode);
1474 begin
1475 if Node=nil then exit;
1476 AddContextsBackwards(Tool,Node.Parent);
1477 case Node.Desc of
1478 ctnUnit, ctnPackage, ctnProgram, ctnLibrary:
1479 AddContext(pihcSourceName,Tool.GetSourceName);
1480 ctnVarDefinition:
1481 AddContext(pihcVariable,Tool.ExtractDefinitionName(Node));
1482 ctnTypeDefinition:
1483 AddContext(pihcType,Tool.ExtractDefinitionName(Node));
1484 ctnConstDefinition:
1485 AddContext(pihcConst,Tool.ExtractDefinitionName(Node));
1486 ctnProperty:
1487 AddContext(pihcProperty,Tool.ExtractPropName(Node,false));
1488 ctnProcedure:
1489 AddContext(pihcProcedure,Tool.ExtractProcName(Node,
1490 [phpWithoutClassName]));
1491 ctnProcedureHead:
1492 AddContext(pihcParameterList,Tool.ExtractProcHead(Node,
1493 [phpWithoutClassKeyword,phpWithoutClassName,phpWithoutName,
1494 phpWithoutSemicolon]));
1495 end;
1496 end;
1497
1498 var
1499 MainCodeBuffer: TCodeBuffer;
1500 Tool: TCustomCodeTool;
1501 CleanPos: integer;
1502 i: Integer;
1503 Node: TCodeTreeNode;
1504 IncludeChain: TFPList;
1505 ConversionResult: LongInt;
1506 begin
1507 Result:=nil;
1508 // find code buffer
1509 if ACodePos^.Code=nil then begin
1510 debugln('WARNING: ConvertCodePosToPascalHelpContext ACodePos.Code=nil');
1511 exit;
1512 end;
1513 Result:=TPascalHelpContextList.Create;
1514 // add filename and all filenames of the include chain
1515 IncludeChain:=nil;
1516 try
1517 CodeToolBoss.GetIncludeCodeChain(ACodePos^.Code,true,IncludeChain);
1518 if IncludeChain=nil then begin
1519 debugln('WARNING: ConvertCodePosToPascalHelpContext IncludeChain=nil');
1520 exit;
1521 end;
1522 for i:=0 to IncludeChain.Count-1 do
1523 AddContext(pihcFilename,TCodeBuffer(IncludeChain[i]).Filename);
1524 MainCodeBuffer:=TCodeBuffer(IncludeChain[0]);
1525 finally
1526 IncludeChain.Free;
1527 end;
1528 // find code tool
1529 Tool:=CodeToolBoss.FindCodeToolForSource(MainCodeBuffer);
1530 if not (Tool is TCodeTool) then begin
1531 debugln('WARNING: ConvertCodePosToPascalHelpContext not (Tool is TCodeTool) MainCodeBuffer=',MainCodeBuffer.Filename);
1532 exit;
1533 end;
1534 // convert cursor position to clean position
1535 ConversionResult:=Tool.CaretToCleanPos(ACodePos^,CleanPos);
1536 if ConversionResult<>0 then begin
1537 // position not in clean code, maybe a comment, maybe behind last line
1538 // => ignore
1539 exit;
1540 end;
1541 // find node
1542 Node:=Tool.FindDeepestNodeAtPos(CleanPos,false);
1543 if Node=nil then begin
1544 // position not in a scanned pascal node, maybe in between
1545 // => ignore
1546 exit;
1547 end;
1548 AddContextsBackwards(TCodeTool(Tool),Node);
1549 end;
1550
GetFPDocFilenameForSourcenull1551 function TIDEHelpManager.GetFPDocFilenameForSource(SrcFilename: string;
1552 ResolveIncludeFiles: Boolean; out AnOwner: TObject): string;
1553 var
1554 CacheWasUsed: boolean;
1555 begin
1556 Result:=CodeHelpBoss.GetFPDocFilenameForSource(SrcFilename,ResolveIncludeFiles,
1557 CacheWasUsed,AnOwner);
1558 end;
1559
1560 procedure TIDEHelpManager.ShowHelpForMessage;
1561 var
1562 Line: TMessageLine;
1563 Parts: TStringList;
1564 begin
1565 if IDEMessagesWindow=nil then exit;
1566 Line:=IDEMessagesWindow.GetSelectedLine;
1567 if Line=nil then exit;
1568 Parts:=TStringList.Create;
1569 Line.GetAttributes(Parts);
1570 ShowHelpOrErrorForMessageLine(Line.Msg,Parts);
1571 end;
1572
1573 procedure TIDEHelpManager.ShowHelpForObjectInspector(Sender: TObject);
1574 var
1575 AnInspector: TObjectInspectorDlg;
1576 Code: TCodeBuffer;
1577 Caret: TPoint;
1578 ErrMsg: string;
1579 NewTopLine: integer;
1580 begin
1581 //DebugLn('TIDEHelpManager.ShowHelpForObjectInspector ',dbgsName(Sender));
1582 if Sender=nil then Sender:=ObjectInspector1;
1583 if Sender is TObjectInspectorDlg then begin
1584 AnInspector:=TObjectInspectorDlg(Sender);
1585 if AnInspector.GetActivePropertyRow<>nil then begin
1586 if FindDeclarationOfOIProperty(AnInspector,nil,Code,Caret,NewTopLine) then
1587 begin
1588 if NewTopLine=0 then ;
1589 ErrMsg:='TIDEHelpManager.ShowHelpForObjectInspector ShowHelpForSourcePosition';
1590 ShowHelpForSourcePosition(Code.Filename,Caret,ErrMsg);
1591 end;
1592 end else begin
1593 DebugLn('TIDEHelpManager.ShowHelpForObjectInspector show default help for OI');
1594 ShowHelpForIDEControl(AnInspector);
1595 end;
1596 end;
1597 end;
1598
1599 procedure TIDEHelpManager.ShowHelpForIDEControl(Sender: TControl);
1600 begin
1601 LoadIDEWindowHelp;
1602 IDEWindowHelpNodes.InvokeHelp(Sender);
1603 end;
1604
GetHintForSourcePositionnull1605 function TIDEHelpManager.GetHintForSourcePosition(const ExpandedFilename: string;
1606 const CodePos: TPoint; out BaseURL, HTMLHint: string;
1607 Flags: TIDEHelpManagerCreateHintFlags): TShowHelpResult;
1608 var
1609 Code: TCodeBuffer;
1610 CacheWasUsed: boolean;
1611 HintFlags: TCodeHelpHintOptions;
1612 PropDetails: string;
1613 begin
1614 BaseURL:='';
1615 HTMLHint:='';
1616 Code:=CodeToolBoss.LoadFile(ExpandedFilename,true,false);
1617 if (Code=nil) or Code.LineColIsSpace(CodePos.Y,CodePos.X) then
1618 exit(shrHelpNotFound);
1619 HintFlags:=[chhoDeclarationHeader,chhoComments];
1620 if ihmchAddFocusHint in Flags then
1621 Include(HintFlags,chhoShowFocusHint);
1622 if CodeHelpBoss.GetHTMLHint(Code,CodePos.X,CodePos.Y,
1623 HintFlags,BaseURL,HTMLHint,PropDetails,CacheWasUsed)=chprSuccess
1624 then
1625 exit(shrSuccess);
1626 Result:=shrHelpNotFound;
1627 end;
1628
ConvertSourcePosToPascalHelpContextnull1629 function TIDEHelpManager.ConvertSourcePosToPascalHelpContext(
1630 const CaretPos: TPoint; const Filename: string): TPascalHelpContextList;
1631 var
1632 CodePos: TCodeXYPosition;
1633 Code: TCodeBuffer;
1634 ACodeTool: TCodeTool;
1635 begin
1636 Result:=nil;
1637 Code:=CodeToolBoss.FindFile(Filename);
1638 if Code=nil then exit;
1639 CodePos.Code:=Code;
1640 CodePos.X:=CaretPos.X;
1641 CodePos.Y:=CaretPos.Y;
1642 if not CodeToolBoss.Explore(Code,ACodeTool,false) then exit;
1643 if ACodeTool=nil then ;
1644 Result:=ConvertCodePosToPascalHelpContext(@CodePos);
1645 end;
1646
1647 { TIDEHintWindowManager }
1648
HintIsComplexnull1649 function TIDEHintWindowManager.HintIsComplex: boolean;
1650 begin
1651 Result := HintIsVisible and (CurHintWindow.ControlCount > 0)
1652 and not (CurHintWindow.Controls[0] is TSimpleHTMLControl);
1653 end;
1654
PtIsOnHintnull1655 function TIDEHintWindowManager.PtIsOnHint(Pt: TPoint): boolean;
1656 begin
1657 Result := PtInRect(CurHintWindow.BoundsRect, Pt);
1658 end;
1659
SenderIsHintControlnull1660 function TIDEHintWindowManager.SenderIsHintControl(Sender: TObject): Boolean;
1661 // ToDo: simplify. FHintWindow only has one child control.
1662
1663 function IsHintControl(Control: TWinControl): Boolean;
1664 var
1665 I: Integer;
1666 begin
1667 if not Control.Visible then
1668 Exit(False);
1669 Result := Control = Sender;
1670 if Result then
1671 Exit;
1672 for I := 0 to Control.ControlCount - 1 do
1673 begin
1674 Result := Control.Controls[I] = Sender;
1675 if Result then
1676 Exit;
1677 if (Control.Controls[I] is TWinControl) then
1678 begin
1679 Result := IsHintControl(TWinControl(Control.Controls[I]));
1680 if Result then
1681 Exit;
1682 end;
1683 end;
1684 end;
1685
1686 begin
1687 if Assigned(CurHintWindow) then
1688 Assert(CurHintWindow.ControlCount < 2,
1689 'SenderIsHintControl: ControlCount = ' + IntToStr(CurHintWindow.ControlCount));
1690 Result := Assigned(Sender) and Assigned(CurHintWindow) and IsHintControl(CurHintWindow);
1691 end;
1692
1693
1694 initialization
1695 RegisterPropertyEditor(TypeInfo(AnsiString),
1696 THTMLBrowserHelpViewer,'BrowserPath',TFileNamePropertyEditor);
1697
1698 end.
1699
1700