whelp.pas 25 KB

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