whelp.pas 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006
  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. 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. private
  107. procedure MaintainTopicCache;
  108. end;
  109. PHelpFileCollection = PCollection;
  110. PHelpFacility = ^THelpFacility;
  111. THelpFacility = object(TObject)
  112. HelpFiles: PHelpFileCollection;
  113. IndexTabSize: sw_integer;
  114. constructor Init;
  115. function AddFile(const FileName, Param: string): PHelpFile;
  116. function AddHelpFile(H: PHelpFile): boolean;
  117. function LoadTopic(SourceFileID: word; Context: THelpCtx): PTopic; virtual;
  118. function TopicSearch(Keyword: string; var FileID: word; var Context: THelpCtx): boolean; virtual;
  119. function BuildIndexTopic: PTopic; virtual;
  120. destructor Done; virtual;
  121. private
  122. LastID: word;
  123. function SearchFile(ID: byte): PHelpFile;
  124. function SearchTopicInHelpFile(F: PHelpFile; Context: THelpCtx): PTopic;
  125. function SearchTopicOwner(SourceFileID: word; Context: THelpCtx): PHelpFile;
  126. end;
  127. THelpFileOpenProc = function(const FileName,Param: string;Index : longint): PHelpFile;
  128. PHelpFileType = ^THelpFileType;
  129. THelpFileType = record
  130. OpenProc : THelpFileOpenProc;
  131. end;
  132. const TopicCacheSize : sw_integer = 10;
  133. HelpStreamBufSize : sw_integer = 4096;
  134. HelpFacility : PHelpFacility = nil;
  135. MaxHelpTopicSize : sw_word = {$ifdef FPC}3*65520{$else}65520{$endif};
  136. function NewTopic(FileID: byte; HelpCtx: THelpCtx; Pos: longint; Param: string;
  137. ExtData: pointer; ExtDataSize: longint): PTopic;
  138. procedure DisposeTopic(P: PTopic);
  139. procedure RenderTopic(Lines: PUnsortedStringCollection; T: PTopic);
  140. procedure BuildTopic(Lines: PUnsortedStringCollection; T: PTopic);
  141. procedure AddLinkToTopic(T: PTopic; AFileID: word; ACtx: THelpCtx);
  142. function NewIndexEntry(Tag: string; FileID: word; HelpCtx: THelpCtx): PIndexEntry;
  143. procedure DisposeIndexEntry(P: PIndexEntry);
  144. procedure DisposeRecord(var R: TRecord);
  145. procedure RegisterHelpFileType(AOpenProc: THelpFileOpenProc);
  146. function GetHelpFileTypeCount: integer;
  147. procedure GetHelpFileType(Index: sw_integer; var HT: THelpFileType);
  148. procedure DoneHelpFilesTypes;
  149. implementation
  150. uses
  151. {$ifdef Unix}
  152. {$ifdef VER1_0}
  153. linux,
  154. {$else}
  155. unix,
  156. {$endif}
  157. {$endif Unix}
  158. {$IFDEF OS2}
  159. DosCalls,
  160. {$ENDIF OS2}
  161. WConsts;
  162. type
  163. PHelpFileTypeCollection = ^THelpFileTypeCollection;
  164. THelpFileTypeCollection = object(TCollection)
  165. function At(Index: sw_Integer): PHelpFileType;
  166. procedure FreeItem(Item: Pointer); virtual;
  167. end;
  168. const
  169. HelpFileTypes : PHelpFileTypeCollection = nil;
  170. function NewHelpFileType(AOpenProc: THelpFileOpenProc): PHelpFileType;
  171. var P: PHelpFileType;
  172. begin
  173. New(P);
  174. with P^ do begin OpenProc:=AOpenProc; end;
  175. NewHelpFileType:=P;
  176. end;
  177. procedure DisposeHelpFileType(P: PHelpFileType);
  178. begin
  179. if Assigned(P) then
  180. Dispose(P);
  181. end;
  182. procedure DoneHelpFilesTypes;
  183. begin
  184. if Assigned(HelpFileTypes) then
  185. Dispose(HelpFileTypes, Done);
  186. end;
  187. function THelpFileTypeCollection.At(Index: sw_Integer): PHelpFileType;
  188. begin
  189. At:=inherited At(Index);
  190. end;
  191. procedure THelpFileTypeCollection.FreeItem(Item: Pointer);
  192. begin
  193. if Assigned(Item) then
  194. DisposeHelpFileType(Item);
  195. end;
  196. procedure RegisterHelpFileType(AOpenProc: THelpFileOpenProc);
  197. begin
  198. if not Assigned(HelpFileTypes) then
  199. New(HelpFileTypes, Init(10,10));
  200. HelpFileTypes^.Insert(NewHelpFileType(AOpenProc));
  201. end;
  202. function GetHelpFileTypeCount: integer;
  203. var Count: integer;
  204. begin
  205. if not Assigned(HelpFileTypes) then
  206. Count:=0
  207. else
  208. Count:=HelpFileTypes^.Count;
  209. GetHelpFileTypeCount:=Count;
  210. end;
  211. procedure GetHelpFileType(Index: sw_integer; var HT: THelpFileType);
  212. begin
  213. HT:=HelpFileTypes^.At(Index)^;
  214. end;
  215. Function GetDosTicks:longint; { returns ticks at 18.2 Hz, just like DOS }
  216. {$IFDEF OS2}
  217. const
  218. QSV_MS_COUNT = 14;
  219. var
  220. L: longint;
  221. begin
  222. DosQuerySysInfo (QSV_MS_COUNT, QSV_MS_COUNT, L, 4);
  223. GetDosTicks := L div 55;
  224. end;
  225. {$ENDIF}
  226. {$IFDEF Unix}
  227. var
  228. tv : TimeVal;
  229. tz : TimeZone;
  230. begin
  231. GetTimeOfDay(tv); {Timezone no longer used?}
  232. GetDosTicks:=((tv.Sec mod 86400) div 60)*1092+((tv.Sec mod 60)*1000000+tv.USec) div 54945;
  233. end;
  234. {$endif Unix}
  235. {$ifdef Win32}
  236. begin
  237. GetDosTicks:=(Windows.GetTickCount*5484) div 100;
  238. end;
  239. {$endif Win32}
  240. {$ifdef go32v2}
  241. begin
  242. GetDosTicks:=MemL[$40:$6c];
  243. end;
  244. {$endif go32v2}
  245. {$ifdef TP}
  246. begin
  247. GetDosTicks:=MemL[$40:$6c];
  248. end;
  249. {$endif go32v2}
  250. procedure DisposeRecord(var R: TRecord);
  251. begin
  252. with R do
  253. if (Size>0) and (Data<>nil) then FreeMem(Data, Size);
  254. FillChar(R, SizeOf(R), 0);
  255. end;
  256. function NewTopic(FileID: byte; HelpCtx: THelpCtx; Pos: longint; Param: string;
  257. ExtData: pointer; ExtDataSize: longint): PTopic;
  258. var P: PTopic;
  259. begin
  260. New(P); FillChar(P^,SizeOf(P^), 0);
  261. P^.HelpCtx:=HelpCtx; P^.FileOfs:=Pos; P^.FileID:=FileID;
  262. P^.Param:=NewStr(Param);
  263. if Assigned(ExtData) and (ExtDataSize>0) then
  264. begin
  265. P^.ExtDataSize:=ExtDataSize;
  266. GetMem(P^.ExtData,ExtDataSize);
  267. Move(ExtData^,P^.ExtData^,ExtDataSize);
  268. end;
  269. New(P^.NamedMarks, Init(100,100));
  270. NewTopic:=P;
  271. end;
  272. procedure DisposeTopic(P: PTopic);
  273. begin
  274. if P<>nil then
  275. begin
  276. if (P^.TextSize>0) and (P^.Text<>nil) then
  277. FreeMem(P^.Text,P^.TextSize);
  278. P^.Text:=nil;
  279. if {(P^.LinkCount>0) and }(P^.Links<>nil) then
  280. FreeMem(P^.Links,P^.LinkSize);
  281. P^.Links:=nil;
  282. if P^.Param<>nil then DisposeStr(P^.Param); P^.Param:=nil;
  283. if Assigned(P^.ExtData) then
  284. FreeMem(P^.ExtData{$ifndef FPC},P^.ExtDataSize{$endif});
  285. if Assigned(P^.NamedMarks) then Dispose(P^.NamedMarks, Done); P^.NamedMarks:=nil;
  286. Dispose(P);
  287. end;
  288. end;
  289. function CloneTopic(T: PTopic): PTopic;
  290. var NT: PTopic;
  291. procedure CloneMark(P: PString); {$ifndef FPC}far;{$endif}
  292. begin
  293. NT^.NamedMarks^.InsertStr(GetStr(P));
  294. end;
  295. begin
  296. New(NT);
  297. Move(T^,NT^,SizeOf(NT^));
  298. if NT^.Text<>nil then
  299. begin GetMem(NT^.Text,NT^.TextSize); Move(T^.Text^,NT^.Text^,NT^.TextSize); end;
  300. if NT^.Links<>nil then
  301. begin
  302. GetMem(NT^.Links,NT^.LinkSize);
  303. Move(T^.Links^,NT^.Links^,NT^.LinkSize);
  304. end;
  305. if NT^.Param<>nil then
  306. NT^.Param:=NewStr(T^.Param^);
  307. if Assigned(T^.NamedMarks) then
  308. begin
  309. New(NT^.NamedMarks, Init(T^.NamedMarks^.Count,10));
  310. T^.NamedMarks^.ForEach(@CloneMark);
  311. end;
  312. NT^.ExtDataSize:=T^.ExtDataSize;
  313. if Assigned(T^.ExtData) and (T^.ExtDataSize>0) then
  314. begin
  315. GetMem(NT^.ExtData,NT^.ExtDataSize);
  316. Move(T^.ExtData^,NT^.ExtData^,NT^.ExtDataSize);
  317. end;
  318. CloneTopic:=NT;
  319. end;
  320. procedure RenderTopic(Lines: PUnsortedStringCollection; T: PTopic);
  321. var Size,CurPtr,I,MSize: sw_word;
  322. S: string;
  323. begin
  324. CurPtr:=0;
  325. for I:=0 to Lines^.Count-1 do
  326. begin
  327. S:=GetStr(Lines^.At(I));
  328. Size:=length(S)+1;
  329. Inc(CurPtr,Size);
  330. end;
  331. Size:=CurPtr;
  332. T^.TextSize:=Size; GetMem(T^.Text,T^.TextSize);
  333. CurPtr:=0;
  334. for I:=0 to Lines^.Count-1 do
  335. begin
  336. S:=GetStr(Lines^.At(I)); Size:=length(S); MSize:=Size;
  337. if CurPtr+Size>=T^.TextSize then
  338. MSize:=T^.TextSize-CurPtr;
  339. Move(S[1],PByteArray(T^.Text)^[CurPtr],MSize);
  340. if MSize<>Size then
  341. Break;
  342. Inc(CurPtr,Size);
  343. PByteArray(T^.Text)^[CurPtr]:=ord(hscLineBreak);
  344. Inc(CurPtr);
  345. if CurPtr>=T^.TextSize then Break;
  346. end;
  347. end;
  348. procedure BuildTopic(Lines: PUnsortedStringCollection; T: PTopic);
  349. var Size,CurPtr,MSize: sw_word;
  350. I: sw_integer;
  351. S: string;
  352. begin
  353. CurPtr:=0;
  354. for I:=0 to Lines^.Count-1 do
  355. begin
  356. S:=GetStr(Lines^.At(I));
  357. Size:=length(S);
  358. Inc(CurPtr,Size);
  359. end;
  360. Size:=CurPtr;
  361. T^.TextSize:=Size; GetMem(T^.Text,T^.TextSize);
  362. CurPtr:=0;
  363. for I:=0 to Lines^.Count-1 do
  364. begin
  365. S:=GetStr(Lines^.At(I)); Size:=length(S); MSize:=Size;
  366. if Size>0 then
  367. begin
  368. if CurPtr+Size>=T^.TextSize then
  369. MSize:=T^.TextSize-CurPtr;
  370. Move(S[1],PByteArray(T^.Text)^[CurPtr],MSize);
  371. if MSize<>Size then
  372. Break;
  373. Inc(CurPtr,Size);
  374. end;
  375. if CurPtr>=T^.TextSize then Break;
  376. end;
  377. end;
  378. procedure AddLinkToTopic(T: PTopic; AFileID: word; ACtx: THelpCtx);
  379. var NewSize: word;
  380. NewPtr: pointer;
  381. begin
  382. NewSize:=longint(T^.LinkCount+1)*sizeof(T^.Links^[0]);
  383. GetMem(NewPtr,NewSize);
  384. if Assigned(T^.Links) then
  385. begin
  386. Move(T^.Links^,NewPtr^,T^.LinkSize);
  387. FreeMem(T^.Links,T^.LinkSize);
  388. end;
  389. T^.Links:=NewPtr;
  390. with T^.Links^[T^.LinkCount] do
  391. begin
  392. FileID:=AFileID;
  393. Context:=ACtx;
  394. end;
  395. Inc(T^.LinkCount);
  396. end;
  397. function NewIndexEntry(Tag: string; FileID: word; HelpCtx: THelpCtx): PIndexEntry;
  398. var P: PIndexEntry;
  399. begin
  400. New(P); FillChar(P^,SizeOf(P^), 0);
  401. P^.Tag:=NewStr(Tag); P^.FileID:=FileID; P^.HelpCtx:=HelpCtx;
  402. NewIndexEntry:=P;
  403. end;
  404. procedure DisposeIndexEntry(P: PIndexEntry);
  405. begin
  406. if P<>nil then
  407. begin
  408. if P^.Tag<>nil then DisposeStr(P^.Tag);
  409. Dispose(P);
  410. end;
  411. end;
  412. function TTopic.LinkSize: sw_word;
  413. begin
  414. LinkSize:=LinkCount*SizeOf(Links^[0]);
  415. end;
  416. function TTopic.GetNamedMarkIndex(const MarkName: string): sw_integer;
  417. var I,Index: sw_integer;
  418. begin
  419. Index:=-1;
  420. if Assigned(NamedMarks) then
  421. for I:=0 to NamedMarks^.Count-1 do
  422. if CompareText(GetStr(NamedMarks^.At(I)),MarkName)=0 then
  423. begin
  424. Index:=I;
  425. Break;
  426. end;
  427. GetNamedMarkIndex:=Index;
  428. end;
  429. function TTopicCollection.At(Index: sw_Integer): PTopic;
  430. begin
  431. At:=inherited At(Index);
  432. end;
  433. procedure TTopicCollection.FreeItem(Item: Pointer);
  434. begin
  435. if Item<>nil then DisposeTopic(Item);
  436. end;
  437. function TTopicCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
  438. var K1: PTopic absolute Key1;
  439. K2: PTopic absolute Key2;
  440. R: Sw_integer;
  441. begin
  442. if K1^.HelpCtx<K2^.HelpCtx then R:=-1 else
  443. if K1^.HelpCtx>K2^.HelpCtx then R:= 1 else
  444. R:=0;
  445. Compare:=R;
  446. end;
  447. function TTopicCollection.SearchTopic(AHelpCtx: THelpCtx): PTopic;
  448. var T: TTopic;
  449. P: PTopic;
  450. Index: sw_integer;
  451. begin
  452. T.HelpCtx:=AHelpCtx;
  453. if Search(@T,Index) then
  454. P:=At(Index)
  455. else
  456. P:=nil;
  457. SearchTopic:=P;
  458. end;
  459. function TIndexEntryCollection.At(Index: Sw_Integer): PIndexEntry;
  460. begin
  461. At:=inherited At(Index);
  462. end;
  463. procedure TIndexEntryCollection.FreeItem(Item: Pointer);
  464. begin
  465. if Item<>nil then DisposeIndexEntry(Item);
  466. end;
  467. function TUnsortedIndexEntryCollection.At(Index: Sw_Integer): PIndexEntry;
  468. begin
  469. At:=inherited At(Index);
  470. end;
  471. procedure TUnsortedIndexEntryCollection.FreeItem(Item: Pointer);
  472. begin
  473. if Item<>nil then DisposeIndexEntry(Item);
  474. end;
  475. function TIndexEntryCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
  476. var K1: PIndexEntry absolute Key1;
  477. K2: PIndexEntry absolute Key2;
  478. R: Sw_integer;
  479. S1,S2: string;
  480. begin
  481. S1:=UpcaseStr(K1^.Tag^); S2:=UpcaseStr(K2^.Tag^);
  482. if S1<S2 then R:=-1 else
  483. if S1>S2 then R:=1 else
  484. if K1^.FileID<K2^.FileID then R:=-1 else
  485. if K1^.FileID>K2^.FileID then R:= 1 else
  486. R:=0;
  487. Compare:=R;
  488. end;
  489. constructor THelpFile.Init(AID: word);
  490. begin
  491. inherited Init;
  492. ID:=AID;
  493. New(Topics, Init(2000,1000));
  494. New(IndexEntries, Init(2000,1000));
  495. end;
  496. procedure THelpFile.AddTopic(HelpCtx: THelpCtx; Pos: longint; const Param: string; ExtData: pointer; ExtDataSize: longint);
  497. begin
  498. Topics^.Insert(NewTopic(ID,HelpCtx,Pos,Param,ExtData,ExtDataSize));
  499. end;
  500. procedure THelpFile.AddIndexEntry(const Text: string; AHelpCtx: THelpCtx);
  501. begin
  502. IndexEntries^.Insert(NewIndexEntry(Text,ID,AHelpCtx));
  503. end;
  504. function THelpFile.LoadTopic(HelpCtx: THelpCtx): PTopic;
  505. var T: PTopic;
  506. begin
  507. T:=SearchTopic(HelpCtx);
  508. if (T<>nil) then
  509. if T^.Text=nil then
  510. begin
  511. MaintainTopicCache;
  512. if ReadTopic(T)=false then
  513. T:=nil;
  514. if (T<>nil) and (T^.Text=nil) then T:=nil;
  515. end;
  516. if T<>nil then
  517. begin
  518. T^.LastAccess:=GetDosTicks;
  519. T:=CloneTopic(T);
  520. end;
  521. LoadTopic:=T;
  522. end;
  523. function THelpFile.LoadIndex: boolean;
  524. begin
  525. Abstract;
  526. LoadIndex:=false; { remove warning }
  527. end;
  528. function THelpFile.SearchTopic(HelpCtx: THelpCtx): PTopic;
  529. var T: PTopic;
  530. begin
  531. T:=Topics^.SearchTopic(HelpCtx);
  532. SearchTopic:=T;
  533. end;
  534. function THelpFile.ReadTopic(T: PTopic): boolean;
  535. begin
  536. Abstract;
  537. ReadTopic:=false; { remove warning }
  538. end;
  539. procedure THelpFile.MaintainTopicCache;
  540. var Count: sw_integer;
  541. MinLRU: longint;
  542. procedure CountThem(P: PTopic); {$ifndef FPC}far;{$endif}
  543. begin if (P^.Text<>nil) or (P^.Links<>nil) then Inc(Count); end;
  544. procedure SearchLRU(P: PTopic); {$ifndef FPC}far;{$endif}
  545. begin if P^.LastAccess<MinLRU then begin MinLRU:=P^.LastAccess; end; end;
  546. var P: PTopic;
  547. begin
  548. Count:=0; Topics^.ForEach(@CountThem);
  549. if (Count>=TopicCacheSize) then
  550. begin
  551. MinLRU:=MaxLongint; P:=nil; Topics^.ForEach(@SearchLRU);
  552. if P<>nil then
  553. begin
  554. FreeMem(P^.Text,P^.TextSize); P^.TextSize:=0; P^.Text:=nil;
  555. FreeMem(P^.Links,P^.LinkSize); P^.LinkCount:=0; P^.Links:=nil;
  556. end;
  557. end;
  558. end;
  559. destructor THelpFile.Done;
  560. begin
  561. if Topics<>nil then Dispose(Topics, Done);
  562. if IndexEntries<>nil then Dispose(IndexEntries, Done);
  563. inherited Done;
  564. end;
  565. constructor THelpFacility.Init;
  566. begin
  567. inherited Init;
  568. New(HelpFiles, Init(10,10));
  569. IndexTabSize:=40;
  570. end;
  571. function THelpFacility.AddFile(const FileName, Param: string): PHelpFile;
  572. var H: PHelpFile;
  573. OK: boolean;
  574. I: integer;
  575. HT: THelpFileType;
  576. begin
  577. OK:=false; H:=nil;
  578. for I:=0 to GetHelpFileTypeCount-1 do
  579. begin
  580. GetHelpFileType(I,HT);
  581. H:=HT.OpenProc(FileName,Param,LastID+1);
  582. if Assigned(H) then
  583. Break;
  584. end;
  585. if Assigned(H) then
  586. OK:=AddHelpFile(H);
  587. if (not OK) and Assigned(H) then begin Dispose(H, Done); H:=nil; end;
  588. AddFile:=H;
  589. end;
  590. function THelpFacility.AddHelpFile(H: PHelpFile): boolean;
  591. begin
  592. if H<>nil then
  593. begin
  594. HelpFiles^.Insert(H);
  595. Inc(LastID);
  596. { H^.ID:=LastID; now already set by OpenProc PM }
  597. end;
  598. AddHelpFile:=H<>nil;
  599. end;
  600. function THelpFacility.SearchTopicOwner(SourceFileID: word; Context: THelpCtx): PHelpFile;
  601. var P: PTopic;
  602. HelpFile: PHelpFile;
  603. function Search(F: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  604. begin
  605. P:=SearchTopicInHelpFile(F,Context); if P<>nil then HelpFile:=F;
  606. Search:=P<>nil;
  607. end;
  608. begin
  609. HelpFile:=nil;
  610. if SourceFileID=0 then P:=nil else
  611. begin
  612. HelpFile:=SearchFile(SourceFileID);
  613. P:=SearchTopicInHelpFile(HelpFile,Context);
  614. end;
  615. if P=nil then HelpFiles^.FirstThat(@Search);
  616. if P=nil then HelpFile:=nil;
  617. SearchTopicOwner:=HelpFile;
  618. end;
  619. function THelpFacility.LoadTopic(SourceFileID: word; Context: THelpCtx): PTopic;
  620. var P: PTopic;
  621. H: PHelpFile;
  622. begin
  623. if (SourceFileID=0) and (Context=0) then
  624. P:=BuildIndexTopic else
  625. begin
  626. H:=SearchTopicOwner(SourceFileID,Context);
  627. if (H=nil) then P:=nil else
  628. P:=H^.LoadTopic(Context);
  629. end;
  630. LoadTopic:=P;
  631. end;
  632. function THelpFacility.TopicSearch(Keyword: string; var FileID: word; var Context: THelpCtx): boolean;
  633. function ScanHelpFile(H: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  634. function Search(P: PIndexEntry): boolean; {$ifndef FPC}far;{$endif}
  635. begin
  636. Search:=copy(UpcaseStr(P^.Tag^),1,length(Keyword))=Keyword;
  637. end;
  638. var P: PIndexEntry;
  639. begin
  640. H^.LoadIndex;
  641. P:=H^.IndexEntries^.FirstThat(@Search);
  642. if P<>nil then begin FileID:=H^.ID; Context:=P^.HelpCtx; end;
  643. ScanHelpFile:=P<>nil;
  644. end;
  645. begin
  646. Keyword:=UpcaseStr(Keyword);
  647. TopicSearch:=HelpFiles^.FirstThat(@ScanHelpFile)<>nil;
  648. end;
  649. function THelpFacility.BuildIndexTopic: PTopic;
  650. var T: PTopic;
  651. Keywords: PIndexEntryCollection;
  652. Lines: PUnsortedStringCollection;
  653. procedure InsertKeywordsOfFile(H: PHelpFile); {$ifndef FPC}far;{$endif}
  654. function InsertKeywords(P: PIndexEntry): boolean; {$ifndef FPC}far;{$endif}
  655. begin
  656. Keywords^.Insert(P);
  657. InsertKeywords:=Keywords^.Count>=MaxCollectionSize;
  658. end;
  659. begin
  660. H^.LoadIndex;
  661. if Keywords^.Count<MaxCollectionSize then
  662. H^.IndexEntries^.FirstThat(@InsertKeywords);
  663. end;
  664. procedure AddLine(S: string);
  665. begin
  666. if S='' then S:=' ';
  667. Lines^.Insert(NewStr(S));
  668. end;
  669. var Line: string;
  670. procedure FlushLine;
  671. begin
  672. if Line<>'' then AddLine(Line); Line:='';
  673. end;
  674. var KWCount,NLFlag: sw_integer;
  675. LastFirstChar: char;
  676. procedure NewSection(FirstChar: char);
  677. begin
  678. if FirstChar<=#64 then FirstChar:=#32;
  679. FlushLine;
  680. AddLine('');
  681. AddLine(FirstChar);
  682. AddLine('');
  683. LastFirstChar:=FirstChar;
  684. NLFlag:=0;
  685. end;
  686. function FormatAlias(Alias: string): string;
  687. var StartP,EndP: sw_integer;
  688. begin
  689. repeat
  690. StartP:=Pos(' ',Alias);
  691. if StartP>0 then
  692. begin
  693. EndP:=StartP;
  694. while (EndP+1<=length(Alias)) and (Alias[EndP+1]=' ') do Inc(EndP);
  695. Alias:=copy(Alias,1,StartP-1)+' | '+copy(Alias,EndP+1,High(Alias));
  696. end;
  697. until StartP=0;
  698. if Assigned(HelpFacility) then
  699. if length(Alias)>IndexTabSize-4 then
  700. Alias:=Trim(copy(Alias,1,IndexTabSize-4-2))+'..';
  701. FormatAlias:=Alias;
  702. end;
  703. procedure AddKeyword(KWS: string);
  704. begin
  705. Inc(KWCount); if KWCount=1 then NLFlag:=0;
  706. if (KWCount=1) or
  707. ( (Upcase(KWS[1])<>LastFirstChar) and ( (LastFirstChar>#64) or (KWS[1]>#64) ) ) then
  708. NewSection(Upcase(KWS[1]));
  709. KWS:=FormatAlias(KWS);
  710. if (NLFlag mod 2)=0
  711. then Line:=' '+#2+KWS+#2
  712. else begin
  713. Line:=RExpand(Line,IndexTabSize)+#2+KWS+#2;
  714. FlushLine;
  715. end;
  716. Inc(NLFlag);
  717. end;
  718. var KW: PIndexEntry;
  719. I: sw_integer;
  720. begin
  721. New(Keywords, Init(5000,5000));
  722. HelpFiles^.ForEach(@InsertKeywordsOfFile);
  723. New(Lines, Init((Keywords^.Count div 2)+100,1000));
  724. T:=NewTopic(0,0,0,'',nil,0);
  725. if HelpFiles^.Count=0 then
  726. begin
  727. AddLine('');
  728. AddLine(' '+msg_nohelpfilesinstalled)
  729. end else
  730. begin
  731. AddLine(' '+msg_helpindex);
  732. KWCount:=0; Line:='';
  733. T^.LinkCount:=Min(Keywords^.Count,MaxBytes div sizeof(T^.Links^[0])-1);
  734. GetMem(T^.Links,T^.LinkSize);
  735. for I:=0 to T^.LinkCount-1 do
  736. begin
  737. KW:=Keywords^.At(I);
  738. AddKeyword(KW^.Tag^);
  739. T^.Links^[I].Context:=longint(KW^.HelpCtx);
  740. T^.Links^[I].FileID:=KW^.FileID;
  741. end;
  742. FlushLine;
  743. AddLine('');
  744. end;
  745. RenderTopic(Lines,T);
  746. Dispose(Lines, Done);
  747. Keywords^.DeleteAll; Dispose(Keywords, Done);
  748. BuildIndexTopic:=T;
  749. end;
  750. function THelpFacility.SearchFile(ID: byte): PHelpFile;
  751. function Match(P: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  752. begin
  753. Match:=(P^.ID=ID);
  754. end;
  755. begin
  756. SearchFile:=HelpFiles^.FirstThat(@Match);
  757. end;
  758. function THelpFacility.SearchTopicInHelpFile(F: PHelpFile; Context: THelpCtx): PTopic;
  759. var P: PTopic;
  760. begin
  761. if F=nil then P:=nil else
  762. P:=F^.SearchTopic(Context);
  763. SearchTopicInHelpFile:=P;
  764. end;
  765. destructor THelpFacility.Done;
  766. begin
  767. inherited Done;
  768. Dispose(HelpFiles, Done);
  769. end;
  770. END.
  771. {
  772. $Log$
  773. Revision 1.2 2001-09-18 11:33:53 pierre
  774. * fix Previous Help Topic
  775. Revision 1.1 2001/08/04 11:30:25 peter
  776. * ide works now with both compiler versions
  777. Revision 1.1.2.6 2001/03/20 00:20:44 pierre
  778. * fix some memory leaks + several small enhancements
  779. Revision 1.1.2.5 2000/11/27 12:06:51 pierre
  780. New bunch of Gabor fixes
  781. Revision 1.1.2.4 2000/11/16 23:13:06 pierre
  782. + support for ANSI substitutes to HTML images in HTML viewer
  783. Revision 1.1.2.3 2000/11/14 09:08:51 marco
  784. * First batch IDE renamefest
  785. Revision 1.1.2.2 2000/11/12 19:48:20 hajny
  786. * OS/2 implementation of GetDosTicks added
  787. Revision 1.1.2.1 2000/09/18 13:20:56 pierre
  788. New bunch of Gabor changes
  789. Revision 1.1 2000/07/13 09:48:37 michael
  790. + Initial import
  791. Revision 1.26 2000/07/03 08:54:54 pierre
  792. * Some enhancements for WinHelp support by G abor
  793. Revision 1.25 2000/06/26 07:29:23 pierre
  794. * new bunch of Gabor's changes
  795. Revision 1.24 2000/06/22 09:07:14 pierre
  796. * Gabor changes: see fixes.txt
  797. Revision 1.23 2000/06/16 08:50:44 pierre
  798. + new bunch of Gabor's changes
  799. Revision 1.22 2000/05/31 20:42:02 pierre
  800. * fixthe TRect problem by 'using' windows before objects
  801. Revision 1.21 2000/05/30 07:18:33 pierre
  802. + colors for HTML help by Gabor
  803. Revision 1.20 2000/05/29 10:44:59 pierre
  804. + New bunch of Gabor's changes: see fixes.txt
  805. Revision 1.19 2000/04/25 08:42:35 pierre
  806. * New Gabor changes : see fixes.txt
  807. Revision 1.18 2000/04/18 11:42:38 pierre
  808. lot of Gabor changes : see fixes.txt
  809. Revision 1.17 2000/02/07 11:47:25 pierre
  810. * Remove 64Kb limitation for FPC by Gabor
  811. Revision 1.16 2000/01/03 14:59:03 marco
  812. * Fixed Linux code that got time of day. Removed Timezone parameter
  813. Revision 1.15 1999/08/16 18:25:29 peter
  814. * Adjusting the selection when the editor didn't contain any line.
  815. * Reserved word recognition redesigned, but this didn't affect the overall
  816. syntax highlight speed remarkably (at least not on my Amd-K6/350).
  817. The syntax scanner loop is a bit slow but the main problem is the
  818. recognition of special symbols. Switching off symbol processing boosts
  819. the performance up to ca. 200%...
  820. * The editor didn't allow copying (for ex to clipboard) of a single character
  821. * 'File|Save as' caused permanently run-time error 3. Not any more now...
  822. * Compiler Messages window (actually the whole desktop) did not act on any
  823. keypress when compilation failed and thus the window remained visible
  824. + Message windows are now closed upon pressing Esc
  825. + At 'Run' the IDE checks whether any sources are modified, and recompiles
  826. only when neccessary
  827. + BlockRead and BlockWrite (Ctrl+K+R/W) implemented in TCodeEditor
  828. + LineSelect (Ctrl+K+L) implemented
  829. * The IDE had problems closing help windows before saving the desktop
  830. Revision 1.14 1999/07/18 16:26:42 florian
  831. * IDE compiles with for Win32 and basic things are working
  832. Revision 1.13 1999/04/13 10:47:51 daniel
  833. * Fixed for Linux
  834. Revision 1.12 1999/04/07 21:56:00 peter
  835. + object support for browser
  836. * html help fixes
  837. * more desktop saving things
  838. * NODEBUG directive to exclude debugger
  839. Revision 1.11 1999/03/16 12:38:16 peter
  840. * tools macro fixes
  841. + tph writer
  842. + first things for resource files
  843. Revision 1.10 1999/03/08 14:58:19 peter
  844. + prompt with dialogs for tools
  845. Revision 1.9 1999/03/03 16:44:05 pierre
  846. * TPH reader fix from Peter
  847. Revision 1.8 1999/03/01 15:42:11 peter
  848. + Added dummy entries for functions not yet implemented
  849. * MenuBar didn't update itself automatically on command-set changes
  850. * Fixed Debugging/Profiling options dialog
  851. * TCodeEditor converts spaces to tabs at save only if efUseTabChars is
  852. set
  853. * efBackSpaceUnindents works correctly
  854. + 'Messages' window implemented
  855. + Added '$CAP MSG()' and '$CAP EDIT' to available tool-macros
  856. + Added TP message-filter support (for ex. you can call GREP thru
  857. GREP2MSG and view the result in the messages window - just like in TP)
  858. * A 'var' was missing from the param-list of THelpFacility.TopicSearch,
  859. so topic search didn't work...
  860. * In FPHELP.PAS there were still context-variables defined as word instead
  861. of THelpCtx
  862. * StdStatusKeys() was missing from the statusdef for help windows
  863. + Topic-title for index-table can be specified when adding a HTML-files
  864. Revision 1.6 1999/02/20 15:18:35 peter
  865. + ctrl-c capture with confirm dialog
  866. + ascii table in the tools menu
  867. + heapviewer
  868. * empty file fixed
  869. * fixed callback routines in fpdebug to have far for tp7
  870. Revision 1.5 1999/02/19 15:43:22 peter
  871. * compatibility fixes for FV
  872. Revision 1.4 1999/02/18 13:44:37 peter
  873. * search fixed
  874. + backward search
  875. * help fixes
  876. * browser updates
  877. Revision 1.3 1999/02/08 10:37:46 peter
  878. + html helpviewer
  879. Revision 1.2 1998/12/28 15:47:56 peter
  880. + Added user screen support, display & window
  881. + Implemented Editor,Mouse Options dialog
  882. + Added location of .INI and .CFG file
  883. + Option (INI) file managment implemented (see bottom of Options Menu)
  884. + Switches updated
  885. + Run program
  886. Revision 1.4 1998/12/22 10:39:55 peter
  887. + options are now written/read
  888. + find and replace routines
  889. }