whelp.pas 26 KB

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