whelp.pas 25 KB

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