whelp.pas 26 KB

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