whelp.pas 25 KB

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