whelp.pas 25 KB

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