whelp.pas 26 KB

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