whelp.pas 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927
  1. {
  2. $Id$
  3. This file is part of the Free Pascal Integrated Development Environment
  4. Copyright (c) 1998 by Berczi Gabor
  5. Help support & Borland OA .HLP reader objects and routines
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$R-}
  13. unit WHelp;
  14. interface
  15. uses
  16. {$ifdef Win32}
  17. { placed here to avoid TRect to be found in windows unit
  18. for win32 target whereas its found in objects unit for other targets PM }
  19. windows,
  20. {$endif Win32}
  21. Objects,
  22. WUtils;
  23. const
  24. hscLineBreak = #0;
  25. hscLink = #2;
  26. hscLineStart = #3;
  27. hscCode = #5;
  28. hscDirect = #6; { add the next char directly }
  29. hscCenter = #10;
  30. hscRight = #11;
  31. hscNamedMark = #12;
  32. hscTextAttr = #13;
  33. hscTextColor = #14;
  34. hscNormText = #15;
  35. hscInImage = #16;
  36. type
  37. THelpCtx = longint;
  38. TRecord = packed record
  39. SClass : word;
  40. Size : word;
  41. Data : pointer;
  42. end;
  43. PIndexEntry = ^TIndexEntry;
  44. TIndexEntry = packed record
  45. Tag : PString;
  46. HelpCtx : THelpCtx;
  47. FileID : word;
  48. end;
  49. PKeywordDescriptor = ^TKeywordDescriptor;
  50. TKeywordDescriptor = packed record
  51. FileID : word;
  52. Context : THelpCtx;
  53. end;
  54. PKeywordDescriptors = ^TKeywordDescriptors;
  55. TKeywordDescriptors = array[0..MaxBytes div sizeof(TKeywordDescriptor)-1] of TKeywordDescriptor;
  56. PTopic = ^TTopic;
  57. TTopic = object
  58. HelpCtx : THelpCtx;
  59. FileOfs : longint;
  60. TextSize : sw_word;
  61. Text : PByteArray;
  62. LinkCount : sw_word;
  63. Links : PKeywordDescriptors;
  64. LastAccess : longint;
  65. FileID : word;
  66. Param : PString;
  67. StartNamedMark: integer;
  68. NamedMarks : PUnsortedStringCollection;
  69. ExtData : pointer;
  70. ExtDataSize : longint;
  71. function LinkSize: sw_word;
  72. function GetNamedMarkIndex(const MarkName: string): sw_integer;
  73. end;
  74. PTopicCollection = ^TTopicCollection;
  75. TTopicCollection = object(TSortedCollection)
  76. function At(Index: sw_Integer): PTopic;
  77. procedure FreeItem(Item: Pointer); virtual;
  78. function Compare(Key1, Key2: Pointer): Sw_Integer; virtual;
  79. function SearchTopic(AHelpCtx: THelpCtx): PTopic;
  80. end;
  81. PIndexEntryCollection = ^TIndexEntryCollection;
  82. TIndexEntryCollection = object(TSortedCollection)
  83. function At(Index: Sw_Integer): PIndexEntry;
  84. procedure FreeItem(Item: Pointer); virtual;
  85. function Compare(Key1, Key2: Pointer): Sw_Integer; virtual;
  86. end;
  87. PUnsortedIndexEntryCollection = ^TUnsortedIndexEntryCollection;
  88. TUnsortedIndexEntryCollection = object(TCollection)
  89. function At(Index: Sw_Integer): PIndexEntry;
  90. procedure FreeItem(Item: Pointer); virtual;
  91. end;
  92. PHelpFile = ^THelpFile;
  93. THelpFile = object(TObject)
  94. ID : word;
  95. Topics : PTopicCollection;
  96. IndexEntries : PUnsortedIndexEntryCollection;
  97. constructor Init(AID: word);
  98. function LoadTopic(HelpCtx: THelpCtx): PTopic; virtual;
  99. procedure AddTopic(HelpCtx: THelpCtx; Pos: longint; const Param: string;
  100. ExtData: pointer; ExtDataSize: longint);
  101. procedure AddIndexEntry(const Text: string; AHelpCtx: THelpCtx);
  102. destructor Done; virtual;
  103. public
  104. function LoadIndex: boolean; virtual;
  105. function SearchTopic(HelpCtx: THelpCtx): PTopic; virtual;
  106. function ReadTopic(T: PTopic): boolean; virtual;
  107. private
  108. procedure MaintainTopicCache;
  109. end;
  110. PHelpFileCollection = PCollection;
  111. PHelpFacility = ^THelpFacility;
  112. THelpFacility = object(TObject)
  113. HelpFiles: PHelpFileCollection;
  114. IndexTabSize: sw_integer;
  115. constructor Init;
  116. function AddFile(const FileName, Param: string): PHelpFile;
  117. function AddHelpFile(H: PHelpFile): boolean;
  118. function LoadTopic(SourceFileID: word; Context: THelpCtx): PTopic; virtual;
  119. function TopicSearch(Keyword: string; var FileID: word; var Context: THelpCtx): boolean; virtual;
  120. function BuildIndexTopic: PTopic; virtual;
  121. destructor Done; virtual;
  122. private
  123. LastID: word;
  124. function SearchFile(ID: byte): PHelpFile;
  125. function SearchTopicInHelpFile(F: PHelpFile; Context: THelpCtx): PTopic;
  126. function SearchTopicOwner(SourceFileID: word; Context: THelpCtx): PHelpFile;
  127. end;
  128. THelpFileOpenProc = function(const FileName,Param: string;Index : longint): PHelpFile;
  129. PHelpFileType = ^THelpFileType;
  130. THelpFileType = record
  131. OpenProc : THelpFileOpenProc;
  132. end;
  133. const TopicCacheSize : sw_integer = 10;
  134. HelpStreamBufSize : sw_integer = 4096;
  135. HelpFacility : PHelpFacility = nil;
  136. MaxHelpTopicSize : sw_word = {$ifdef FPC}3*65520{$else}65520{$endif};
  137. function NewTopic(FileID: byte; HelpCtx: THelpCtx; Pos: longint; Param: string;
  138. ExtData: pointer; ExtDataSize: longint): PTopic;
  139. procedure DisposeTopic(P: PTopic);
  140. procedure RenderTopic(Lines: PUnsortedStringCollection; T: PTopic);
  141. procedure BuildTopic(Lines: PUnsortedStringCollection; T: PTopic);
  142. procedure AddLinkToTopic(T: PTopic; AFileID: word; ACtx: THelpCtx);
  143. function NewIndexEntry(Tag: string; FileID: word; HelpCtx: THelpCtx): PIndexEntry;
  144. procedure DisposeIndexEntry(P: PIndexEntry);
  145. procedure DisposeRecord(var R: TRecord);
  146. procedure RegisterHelpFileType(AOpenProc: THelpFileOpenProc);
  147. function GetHelpFileTypeCount: integer;
  148. procedure GetHelpFileType(Index: sw_integer; var HT: THelpFileType);
  149. procedure DoneHelpFilesTypes;
  150. implementation
  151. uses
  152. {$ifdef Unix}
  153. {$ifdef VER1_0}
  154. linux,
  155. {$else}
  156. unix,
  157. {$endif}
  158. {$endif Unix}
  159. {$IFDEF OS2}
  160. DosCalls,
  161. {$ENDIF OS2}
  162. Strings,
  163. WConsts;
  164. type
  165. PHelpFileTypeCollection = ^THelpFileTypeCollection;
  166. THelpFileTypeCollection = object(TCollection)
  167. function At(Index: sw_Integer): PHelpFileType;
  168. procedure FreeItem(Item: Pointer); virtual;
  169. end;
  170. const
  171. HelpFileTypes : PHelpFileTypeCollection = nil;
  172. function NewHelpFileType(AOpenProc: THelpFileOpenProc): PHelpFileType;
  173. var P: PHelpFileType;
  174. begin
  175. New(P);
  176. with P^ do begin OpenProc:=AOpenProc; end;
  177. NewHelpFileType:=P;
  178. end;
  179. procedure DisposeHelpFileType(P: PHelpFileType);
  180. begin
  181. if Assigned(P) then
  182. Dispose(P);
  183. end;
  184. procedure DoneHelpFilesTypes;
  185. begin
  186. if Assigned(HelpFileTypes) then
  187. Dispose(HelpFileTypes, Done);
  188. end;
  189. function THelpFileTypeCollection.At(Index: sw_Integer): PHelpFileType;
  190. begin
  191. At:=inherited At(Index);
  192. end;
  193. procedure THelpFileTypeCollection.FreeItem(Item: Pointer);
  194. begin
  195. if Assigned(Item) then
  196. DisposeHelpFileType(Item);
  197. end;
  198. procedure RegisterHelpFileType(AOpenProc: THelpFileOpenProc);
  199. begin
  200. if not Assigned(HelpFileTypes) then
  201. New(HelpFileTypes, Init(10,10));
  202. HelpFileTypes^.Insert(NewHelpFileType(AOpenProc));
  203. end;
  204. function GetHelpFileTypeCount: integer;
  205. var Count: integer;
  206. begin
  207. if not Assigned(HelpFileTypes) then
  208. Count:=0
  209. else
  210. Count:=HelpFileTypes^.Count;
  211. GetHelpFileTypeCount:=Count;
  212. end;
  213. procedure GetHelpFileType(Index: sw_integer; var HT: THelpFileType);
  214. begin
  215. HT:=HelpFileTypes^.At(Index)^;
  216. end;
  217. Function GetDosTicks:longint; { returns ticks at 18.2 Hz, just like DOS }
  218. {$IFDEF OS2}
  219. const
  220. QSV_MS_COUNT = 14;
  221. var
  222. L: longint;
  223. begin
  224. DosQuerySysInfo (QSV_MS_COUNT, QSV_MS_COUNT, L, 4);
  225. GetDosTicks := L div 55;
  226. end;
  227. {$ENDIF}
  228. {$IFDEF Unix}
  229. var
  230. tv : TimeVal;
  231. tz : TimeZone;
  232. begin
  233. GetTimeOfDay(tv); {Timezone no longer used?}
  234. GetDosTicks:=((tv.Sec mod 86400) div 60)*1092+((tv.Sec mod 60)*1000000+tv.USec) div 54945;
  235. end;
  236. {$endif Unix}
  237. {$ifdef Win32}
  238. begin
  239. GetDosTicks:=(Windows.GetTickCount*5484) div 100;
  240. end;
  241. {$endif Win32}
  242. {$ifdef go32v2}
  243. begin
  244. GetDosTicks:=MemL[$40:$6c];
  245. end;
  246. {$endif go32v2}
  247. {$ifdef TP}
  248. begin
  249. GetDosTicks:=MemL[$40:$6c];
  250. end;
  251. {$endif go32v2}
  252. procedure DisposeRecord(var R: TRecord);
  253. begin
  254. with R do
  255. if (Size>0) and (Data<>nil) then FreeMem(Data, Size);
  256. FillChar(R, SizeOf(R), 0);
  257. end;
  258. function NewTopic(FileID: byte; HelpCtx: THelpCtx; Pos: longint; Param: string;
  259. ExtData: pointer; ExtDataSize: longint): PTopic;
  260. var P: PTopic;
  261. begin
  262. New(P); FillChar(P^,SizeOf(P^), 0);
  263. P^.HelpCtx:=HelpCtx; P^.FileOfs:=Pos; P^.FileID:=FileID;
  264. P^.Param:=NewStr(Param);
  265. if Assigned(ExtData) and (ExtDataSize>0) then
  266. begin
  267. P^.ExtDataSize:=ExtDataSize;
  268. GetMem(P^.ExtData,ExtDataSize);
  269. Move(ExtData^,P^.ExtData^,ExtDataSize);
  270. end;
  271. New(P^.NamedMarks, Init(100,100));
  272. NewTopic:=P;
  273. end;
  274. procedure DisposeTopic(P: PTopic);
  275. begin
  276. if P<>nil then
  277. begin
  278. if (P^.TextSize>0) and (P^.Text<>nil) then
  279. FreeMem(P^.Text,P^.TextSize);
  280. P^.Text:=nil;
  281. if {(P^.LinkCount>0) and }(P^.Links<>nil) then
  282. FreeMem(P^.Links,P^.LinkSize);
  283. P^.Links:=nil;
  284. if P^.Param<>nil then DisposeStr(P^.Param); P^.Param:=nil;
  285. if Assigned(P^.ExtData) then
  286. FreeMem(P^.ExtData{$ifndef FPC},P^.ExtDataSize{$endif});
  287. if Assigned(P^.NamedMarks) then Dispose(P^.NamedMarks, Done); P^.NamedMarks:=nil;
  288. Dispose(P);
  289. end;
  290. end;
  291. function CloneTopic(T: PTopic): PTopic;
  292. var NT: PTopic;
  293. procedure CloneMark(P: PString); {$ifndef FPC}far;{$endif}
  294. begin
  295. NT^.NamedMarks^.InsertStr(GetStr(P));
  296. end;
  297. begin
  298. New(NT);
  299. Move(T^,NT^,SizeOf(NT^));
  300. if NT^.Text<>nil then
  301. begin GetMem(NT^.Text,NT^.TextSize); Move(T^.Text^,NT^.Text^,NT^.TextSize); end;
  302. if NT^.Links<>nil then
  303. begin
  304. GetMem(NT^.Links,NT^.LinkSize);
  305. Move(T^.Links^,NT^.Links^,NT^.LinkSize);
  306. end;
  307. if NT^.Param<>nil then
  308. NT^.Param:=NewStr(T^.Param^);
  309. if Assigned(T^.NamedMarks) then
  310. begin
  311. New(NT^.NamedMarks, Init(T^.NamedMarks^.Count,10));
  312. T^.NamedMarks^.ForEach(@CloneMark);
  313. end;
  314. NT^.ExtDataSize:=T^.ExtDataSize;
  315. if Assigned(T^.ExtData) and (T^.ExtDataSize>0) then
  316. begin
  317. GetMem(NT^.ExtData,NT^.ExtDataSize);
  318. Move(T^.ExtData^,NT^.ExtData^,NT^.ExtDataSize);
  319. end;
  320. CloneTopic:=NT;
  321. end;
  322. procedure RenderTopic(Lines: PUnsortedStringCollection; T: PTopic);
  323. var Size,CurPtr,I,MSize: sw_word;
  324. S: string;
  325. begin
  326. CurPtr:=0;
  327. for I:=0 to Lines^.Count-1 do
  328. begin
  329. S:=GetStr(Lines^.At(I));
  330. Size:=length(S)+1;
  331. Inc(CurPtr,Size);
  332. end;
  333. Size:=CurPtr;
  334. T^.TextSize:=Size; GetMem(T^.Text,T^.TextSize);
  335. CurPtr:=0;
  336. for I:=0 to Lines^.Count-1 do
  337. begin
  338. S:=GetStr(Lines^.At(I)); Size:=length(S); MSize:=Size;
  339. if CurPtr+Size>=T^.TextSize then
  340. MSize:=T^.TextSize-CurPtr;
  341. Move(S[1],PByteArray(T^.Text)^[CurPtr],MSize);
  342. if MSize<>Size then
  343. Break;
  344. Inc(CurPtr,Size);
  345. PByteArray(T^.Text)^[CurPtr]:=ord(hscLineBreak);
  346. Inc(CurPtr);
  347. if CurPtr>=T^.TextSize then Break;
  348. end;
  349. end;
  350. procedure BuildTopic(Lines: PUnsortedStringCollection; T: PTopic);
  351. var Size,CurPtr,MSize: sw_word;
  352. I: sw_integer;
  353. S: string;
  354. begin
  355. CurPtr:=0;
  356. for I:=0 to Lines^.Count-1 do
  357. begin
  358. S:=GetStr(Lines^.At(I));
  359. Size:=length(S);
  360. Inc(CurPtr,Size);
  361. end;
  362. Size:=CurPtr;
  363. T^.TextSize:=Size; GetMem(T^.Text,T^.TextSize);
  364. CurPtr:=0;
  365. for I:=0 to Lines^.Count-1 do
  366. begin
  367. S:=GetStr(Lines^.At(I)); Size:=length(S); MSize:=Size;
  368. if Size>0 then
  369. begin
  370. if CurPtr+Size>=T^.TextSize then
  371. MSize:=T^.TextSize-CurPtr;
  372. Move(S[1],PByteArray(T^.Text)^[CurPtr],MSize);
  373. if MSize<>Size then
  374. Break;
  375. Inc(CurPtr,Size);
  376. end;
  377. if CurPtr>=T^.TextSize then Break;
  378. end;
  379. end;
  380. procedure AddLinkToTopic(T: PTopic; AFileID: word; ACtx: THelpCtx);
  381. var NewSize: word;
  382. NewPtr: pointer;
  383. begin
  384. NewSize:=longint(T^.LinkCount+1)*sizeof(T^.Links^[0]);
  385. GetMem(NewPtr,NewSize);
  386. if Assigned(T^.Links) then
  387. begin
  388. Move(T^.Links^,NewPtr^,T^.LinkSize);
  389. FreeMem(T^.Links,T^.LinkSize);
  390. end;
  391. T^.Links:=NewPtr;
  392. with T^.Links^[T^.LinkCount] do
  393. begin
  394. FileID:=AFileID;
  395. Context:=ACtx;
  396. end;
  397. Inc(T^.LinkCount);
  398. end;
  399. function NewIndexEntry(Tag: string; FileID: word; HelpCtx: THelpCtx): PIndexEntry;
  400. var P: PIndexEntry;
  401. begin
  402. New(P); FillChar(P^,SizeOf(P^), 0);
  403. P^.Tag:=NewStr(Tag); P^.FileID:=FileID; P^.HelpCtx:=HelpCtx;
  404. NewIndexEntry:=P;
  405. end;
  406. procedure DisposeIndexEntry(P: PIndexEntry);
  407. begin
  408. if P<>nil then
  409. begin
  410. if P^.Tag<>nil then DisposeStr(P^.Tag);
  411. Dispose(P);
  412. end;
  413. end;
  414. function TTopic.LinkSize: sw_word;
  415. begin
  416. LinkSize:=LinkCount*SizeOf(Links^[0]);
  417. end;
  418. function TTopic.GetNamedMarkIndex(const MarkName: string): sw_integer;
  419. var I,Index: sw_integer;
  420. begin
  421. Index:=-1;
  422. if Assigned(NamedMarks) then
  423. for I:=0 to NamedMarks^.Count-1 do
  424. if CompareText(GetStr(NamedMarks^.At(I)),MarkName)=0 then
  425. begin
  426. Index:=I;
  427. Break;
  428. end;
  429. GetNamedMarkIndex:=Index;
  430. end;
  431. function TTopicCollection.At(Index: sw_Integer): PTopic;
  432. begin
  433. At:=inherited At(Index);
  434. end;
  435. procedure TTopicCollection.FreeItem(Item: Pointer);
  436. begin
  437. if Item<>nil then DisposeTopic(Item);
  438. end;
  439. function TTopicCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
  440. var K1: PTopic absolute Key1;
  441. K2: PTopic absolute Key2;
  442. R: Sw_integer;
  443. begin
  444. if K1^.HelpCtx<K2^.HelpCtx then R:=-1 else
  445. if K1^.HelpCtx>K2^.HelpCtx then R:= 1 else
  446. R:=0;
  447. Compare:=R;
  448. end;
  449. function TTopicCollection.SearchTopic(AHelpCtx: THelpCtx): PTopic;
  450. var T: TTopic;
  451. P: PTopic;
  452. Index: sw_integer;
  453. begin
  454. T.HelpCtx:=AHelpCtx;
  455. if Search(@T,Index) then
  456. P:=At(Index)
  457. else
  458. P:=nil;
  459. SearchTopic:=P;
  460. end;
  461. function TIndexEntryCollection.At(Index: Sw_Integer): PIndexEntry;
  462. begin
  463. At:=inherited At(Index);
  464. end;
  465. procedure TIndexEntryCollection.FreeItem(Item: Pointer);
  466. begin
  467. if Item<>nil then DisposeIndexEntry(Item);
  468. end;
  469. function TUnsortedIndexEntryCollection.At(Index: Sw_Integer): PIndexEntry;
  470. begin
  471. At:=inherited At(Index);
  472. end;
  473. procedure TUnsortedIndexEntryCollection.FreeItem(Item: Pointer);
  474. begin
  475. if Item<>nil then DisposeIndexEntry(Item);
  476. end;
  477. function TIndexEntryCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
  478. var K1: PIndexEntry absolute Key1;
  479. K2: PIndexEntry absolute Key2;
  480. R: Sw_integer;
  481. S1,S2: string;
  482. T1,T2 : PTopic;
  483. begin
  484. S1:=UpcaseStr(K1^.Tag^); S2:=UpcaseStr(K2^.Tag^);
  485. if S1<S2 then
  486. begin
  487. Compare:=-1;
  488. exit;
  489. end;
  490. if S1>S2 then
  491. begin
  492. Compare:=1;
  493. exit;
  494. end;
  495. (* if assigned(HelpFacility) then
  496. begin
  497. { Try to read the title of the topic }
  498. T1:=HelpFacility^.LoadTopic(K1^.FileID,K1^.HelpCtx);
  499. T2:=HelpFacility^.LoadTopic(K2^.FileID,K2^.HelpCtx);
  500. if assigned(T1^.Text) and assigned(T2^.Text) then
  501. r:=strcomp(pchar(T1^.Text),pchar(T2^.Text))
  502. else
  503. r:=0;
  504. if r>0 then
  505. begin
  506. Compare:=1;
  507. exit;
  508. end;
  509. if r<0 then
  510. begin
  511. Compare:=-1;
  512. exit;
  513. end;
  514. end; *)
  515. if K1^.FileID<K2^.FileID then R:=-1
  516. else if K1^.FileID>K2^.FileID then R:= 1
  517. else if K1^.HelpCtx<K2^.HelpCtx then
  518. r:=-1
  519. else if K1^.HelpCtx>K2^.HelpCtx then
  520. r:=1
  521. else
  522. R:=0;
  523. Compare:=R;
  524. end;
  525. constructor THelpFile.Init(AID: word);
  526. begin
  527. inherited Init;
  528. ID:=AID;
  529. New(Topics, Init(2000,1000));
  530. New(IndexEntries, Init(2000,1000));
  531. end;
  532. procedure THelpFile.AddTopic(HelpCtx: THelpCtx; Pos: longint; const Param: string; ExtData: pointer; ExtDataSize: longint);
  533. begin
  534. Topics^.Insert(NewTopic(ID,HelpCtx,Pos,Param,ExtData,ExtDataSize));
  535. end;
  536. procedure THelpFile.AddIndexEntry(const Text: string; AHelpCtx: THelpCtx);
  537. begin
  538. IndexEntries^.Insert(NewIndexEntry(Text,ID,AHelpCtx));
  539. end;
  540. function THelpFile.LoadTopic(HelpCtx: THelpCtx): PTopic;
  541. var T: PTopic;
  542. begin
  543. T:=SearchTopic(HelpCtx);
  544. if (T<>nil) then
  545. if T^.Text=nil then
  546. begin
  547. MaintainTopicCache;
  548. if ReadTopic(T)=false then
  549. T:=nil;
  550. if (T<>nil) and (T^.Text=nil) then T:=nil;
  551. end;
  552. if T<>nil then
  553. begin
  554. T^.LastAccess:=GetDosTicks;
  555. T:=CloneTopic(T);
  556. end;
  557. LoadTopic:=T;
  558. end;
  559. function THelpFile.LoadIndex: boolean;
  560. begin
  561. Abstract;
  562. LoadIndex:=false; { remove warning }
  563. end;
  564. function THelpFile.SearchTopic(HelpCtx: THelpCtx): PTopic;
  565. var T: PTopic;
  566. begin
  567. T:=Topics^.SearchTopic(HelpCtx);
  568. SearchTopic:=T;
  569. end;
  570. function THelpFile.ReadTopic(T: PTopic): boolean;
  571. begin
  572. Abstract;
  573. ReadTopic:=false; { remove warning }
  574. end;
  575. procedure THelpFile.MaintainTopicCache;
  576. var Count: sw_integer;
  577. MinLRU: longint;
  578. procedure CountThem(P: PTopic); {$ifndef FPC}far;{$endif}
  579. begin if (P^.Text<>nil) or (P^.Links<>nil) then Inc(Count); end;
  580. procedure SearchLRU(P: PTopic); {$ifndef FPC}far;{$endif}
  581. begin if P^.LastAccess<MinLRU then begin MinLRU:=P^.LastAccess; end; end;
  582. var P: PTopic;
  583. begin
  584. Count:=0; Topics^.ForEach(@CountThem);
  585. if (Count>=TopicCacheSize) then
  586. begin
  587. MinLRU:=MaxLongint; P:=nil; Topics^.ForEach(@SearchLRU);
  588. if P<>nil then
  589. begin
  590. FreeMem(P^.Text,P^.TextSize); P^.TextSize:=0; P^.Text:=nil;
  591. FreeMem(P^.Links,P^.LinkSize); P^.LinkCount:=0; P^.Links:=nil;
  592. end;
  593. end;
  594. end;
  595. destructor THelpFile.Done;
  596. begin
  597. if Topics<>nil then Dispose(Topics, Done);
  598. if IndexEntries<>nil then Dispose(IndexEntries, Done);
  599. inherited Done;
  600. end;
  601. constructor THelpFacility.Init;
  602. begin
  603. inherited Init;
  604. New(HelpFiles, Init(10,10));
  605. IndexTabSize:=40;
  606. end;
  607. function THelpFacility.AddFile(const FileName, Param: string): PHelpFile;
  608. var H: PHelpFile;
  609. OK: boolean;
  610. I: integer;
  611. HT: THelpFileType;
  612. begin
  613. OK:=false; H:=nil;
  614. for I:=0 to GetHelpFileTypeCount-1 do
  615. begin
  616. GetHelpFileType(I,HT);
  617. H:=HT.OpenProc(FileName,Param,LastID+1);
  618. if Assigned(H) then
  619. Break;
  620. end;
  621. if Assigned(H) then
  622. OK:=AddHelpFile(H);
  623. if (not OK) and Assigned(H) then begin Dispose(H, Done); H:=nil; end;
  624. AddFile:=H;
  625. end;
  626. function THelpFacility.AddHelpFile(H: PHelpFile): boolean;
  627. begin
  628. if H<>nil then
  629. begin
  630. HelpFiles^.Insert(H);
  631. Inc(LastID);
  632. { H^.ID:=LastID; now already set by OpenProc PM }
  633. end;
  634. AddHelpFile:=H<>nil;
  635. end;
  636. function THelpFacility.SearchTopicOwner(SourceFileID: word; Context: THelpCtx): PHelpFile;
  637. var P: PTopic;
  638. HelpFile: PHelpFile;
  639. function Search(F: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  640. begin
  641. P:=SearchTopicInHelpFile(F,Context); if P<>nil then HelpFile:=F;
  642. Search:=P<>nil;
  643. end;
  644. begin
  645. HelpFile:=nil;
  646. if SourceFileID=0 then P:=nil else
  647. begin
  648. HelpFile:=SearchFile(SourceFileID);
  649. P:=SearchTopicInHelpFile(HelpFile,Context);
  650. end;
  651. if P=nil then HelpFiles^.FirstThat(@Search);
  652. if P=nil then HelpFile:=nil;
  653. SearchTopicOwner:=HelpFile;
  654. end;
  655. function THelpFacility.LoadTopic(SourceFileID: word; Context: THelpCtx): PTopic;
  656. var P: PTopic;
  657. H: PHelpFile;
  658. begin
  659. if (SourceFileID=0) and (Context=0) then
  660. P:=BuildIndexTopic else
  661. begin
  662. H:=SearchTopicOwner(SourceFileID,Context);
  663. if (H=nil) then P:=nil else
  664. P:=H^.LoadTopic(Context);
  665. end;
  666. LoadTopic:=P;
  667. end;
  668. function THelpFacility.TopicSearch(Keyword: string; var FileID: word; var Context: THelpCtx): boolean;
  669. function ScanHelpFile(H: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  670. function Search(P: PIndexEntry): boolean; {$ifndef FPC}far;{$endif}
  671. begin
  672. Search:=copy(UpcaseStr(P^.Tag^),1,length(Keyword))=Keyword;
  673. end;
  674. var P: PIndexEntry;
  675. begin
  676. H^.LoadIndex;
  677. P:=H^.IndexEntries^.FirstThat(@Search);
  678. if P<>nil then begin FileID:=H^.ID; Context:=P^.HelpCtx; end;
  679. ScanHelpFile:=P<>nil;
  680. end;
  681. begin
  682. Keyword:=UpcaseStr(Keyword);
  683. TopicSearch:=HelpFiles^.FirstThat(@ScanHelpFile)<>nil;
  684. end;
  685. function THelpFacility.BuildIndexTopic: PTopic;
  686. var T: PTopic;
  687. Keywords: PIndexEntryCollection;
  688. Lines: PUnsortedStringCollection;
  689. procedure InsertKeywordsOfFile(H: PHelpFile); {$ifndef FPC}far;{$endif}
  690. function InsertKeywords(P: PIndexEntry): boolean; {$ifndef FPC}far;{$endif}
  691. begin
  692. Keywords^.Insert(P);
  693. InsertKeywords:=Keywords^.Count>=MaxCollectionSize;
  694. end;
  695. begin
  696. H^.LoadIndex;
  697. if Keywords^.Count<MaxCollectionSize then
  698. H^.IndexEntries^.FirstThat(@InsertKeywords);
  699. end;
  700. procedure AddLine(S: string);
  701. begin
  702. if S='' then S:=' ';
  703. Lines^.Insert(NewStr(S));
  704. end;
  705. var Line: string;
  706. procedure FlushLine;
  707. begin
  708. if Line<>'' then AddLine(Line); Line:='';
  709. end;
  710. var KWCount,NLFlag: sw_integer;
  711. LastFirstChar: char;
  712. procedure NewSection(FirstChar: char);
  713. begin
  714. if FirstChar<=#64 then FirstChar:=#32;
  715. FlushLine;
  716. AddLine('');
  717. AddLine(FirstChar);
  718. AddLine('');
  719. LastFirstChar:=FirstChar;
  720. NLFlag:=0;
  721. end;
  722. function FormatAlias(Alias: string): string;
  723. var StartP,EndP: sw_integer;
  724. begin
  725. repeat
  726. StartP:=Pos(' ',Alias);
  727. if StartP>0 then
  728. begin
  729. EndP:=StartP;
  730. while (EndP+1<=length(Alias)) and (Alias[EndP+1]=' ') do Inc(EndP);
  731. Alias:=copy(Alias,1,StartP-1)+' | '+copy(Alias,EndP+1,High(Alias));
  732. end;
  733. until StartP=0;
  734. if Assigned(HelpFacility) then
  735. if length(Alias)>IndexTabSize-4 then
  736. Alias:=Trim(copy(Alias,1,IndexTabSize-4-2))+'..';
  737. FormatAlias:=Alias;
  738. end;
  739. procedure AddKeyword(KWS: string);
  740. begin
  741. Inc(KWCount); if KWCount=1 then NLFlag:=0;
  742. if (KWCount=1) or
  743. ( (Upcase(KWS[1])<>LastFirstChar) and ( (LastFirstChar>#64) or (KWS[1]>#64) ) ) then
  744. NewSection(Upcase(KWS[1]));
  745. KWS:=FormatAlias(KWS);
  746. if (NLFlag mod 2)=0
  747. then Line:=' '+#2+KWS+#2
  748. else begin
  749. Line:=RExpand(Line,IndexTabSize)+#2+KWS+#2;
  750. FlushLine;
  751. end;
  752. Inc(NLFlag);
  753. end;
  754. var KW: PIndexEntry;
  755. I,p : sw_integer;
  756. IsMultiple : boolean;
  757. MultiCount : longint;
  758. St,LastTag : String;
  759. begin
  760. New(Keywords, Init(5000,5000));
  761. HelpFiles^.ForEach(@InsertKeywordsOfFile);
  762. New(Lines, Init((Keywords^.Count div 2)+100,1000));
  763. T:=NewTopic(0,0,0,'',nil,0);
  764. if HelpFiles^.Count=0 then
  765. begin
  766. AddLine('');
  767. AddLine(' '+msg_nohelpfilesinstalled)
  768. end else
  769. begin
  770. AddLine(' '+msg_helpindex);
  771. KWCount:=0; Line:='';
  772. T^.LinkCount:=Min(Keywords^.Count,MaxBytes div sizeof(T^.Links^[0])-1);
  773. GetMem(T^.Links,T^.LinkSize);
  774. MultiCount:=0;
  775. LastTag:='';
  776. for I:=0 to T^.LinkCount-1 do
  777. begin
  778. KW:=Keywords^.At(I);
  779. if (LastTag<>KW^.Tag^) then
  780. Begin
  781. MultiCount:=0;
  782. IsMultiple:=(I<T^.LinkCount-1) and (KW^.Tag^=Keywords^.At(I+1)^.Tag^);
  783. End
  784. else
  785. IsMultiple:=true;
  786. if IsMultiple then
  787. Begin
  788. Inc(MultiCount);
  789. (* St:=Trim(strpas(pchar(HelpFacility^.LoadTopic(KW^.FileID,KW^.HelpCtx)^.Text))); *)
  790. St:=KW^.Tag^+' ['+IntToStr(MultiCount)+']';
  791. (* { Remove all special chars }
  792. for p:=1 to Length(st) do
  793. if ord(st[p])<=16 then
  794. st[p]:=' ';
  795. p:=pos(KW^.Tag^,St);
  796. if (p=1) then
  797. AddKeyword(St)
  798. else
  799. AddKeyword(KW^.Tag^+' '+St); *)
  800. AddKeyWord(St);
  801. End
  802. else
  803. AddKeyword(KW^.Tag^);
  804. LastTag:=KW^.Tag^;
  805. T^.Links^[I].Context:=longint(KW^.HelpCtx);
  806. T^.Links^[I].FileID:=KW^.FileID;
  807. end;
  808. FlushLine;
  809. AddLine('');
  810. end;
  811. RenderTopic(Lines,T);
  812. Dispose(Lines, Done);
  813. Keywords^.DeleteAll; Dispose(Keywords, Done);
  814. BuildIndexTopic:=T;
  815. end;
  816. function THelpFacility.SearchFile(ID: byte): PHelpFile;
  817. function Match(P: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  818. begin
  819. Match:=(P^.ID=ID);
  820. end;
  821. begin
  822. SearchFile:=HelpFiles^.FirstThat(@Match);
  823. end;
  824. function THelpFacility.SearchTopicInHelpFile(F: PHelpFile; Context: THelpCtx): PTopic;
  825. var P: PTopic;
  826. begin
  827. if F=nil then P:=nil else
  828. P:=F^.SearchTopic(Context);
  829. SearchTopicInHelpFile:=P;
  830. end;
  831. destructor THelpFacility.Done;
  832. begin
  833. inherited Done;
  834. Dispose(HelpFiles, Done);
  835. end;
  836. END.
  837. {
  838. $Log$
  839. Revision 1.7 2002-09-07 15:40:49 peter
  840. * old logs removed and tabs fixed
  841. Revision 1.6 2002/03/25 14:37:03 pierre
  842. + hscDirect added
  843. }