2
0

whelp.pas 25 KB

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