whelp.pas 25 KB

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