whelp.pas 27 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  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): 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);
  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;
  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.1 2001-08-04 11:30:25 peter
  774. * ide works now with both compiler versions
  775. Revision 1.1.2.6 2001/03/20 00:20:44 pierre
  776. * fix some memory leaks + several small enhancements
  777. Revision 1.1.2.5 2000/11/27 12:06:51 pierre
  778. New bunch of Gabor fixes
  779. Revision 1.1.2.4 2000/11/16 23:13:06 pierre
  780. + support for ANSI substitutes to HTML images in HTML viewer
  781. Revision 1.1.2.3 2000/11/14 09:08:51 marco
  782. * First batch IDE renamefest
  783. Revision 1.1.2.2 2000/11/12 19:48:20 hajny
  784. * OS/2 implementation of GetDosTicks added
  785. Revision 1.1.2.1 2000/09/18 13:20:56 pierre
  786. New bunch of Gabor changes
  787. Revision 1.1 2000/07/13 09:48:37 michael
  788. + Initial import
  789. Revision 1.26 2000/07/03 08:54:54 pierre
  790. * Some enhancements for WinHelp support by G abor
  791. Revision 1.25 2000/06/26 07:29:23 pierre
  792. * new bunch of Gabor's changes
  793. Revision 1.24 2000/06/22 09:07:14 pierre
  794. * Gabor changes: see fixes.txt
  795. Revision 1.23 2000/06/16 08:50:44 pierre
  796. + new bunch of Gabor's changes
  797. Revision 1.22 2000/05/31 20:42:02 pierre
  798. * fixthe TRect problem by 'using' windows before objects
  799. Revision 1.21 2000/05/30 07:18:33 pierre
  800. + colors for HTML help by Gabor
  801. Revision 1.20 2000/05/29 10:44:59 pierre
  802. + New bunch of Gabor's changes: see fixes.txt
  803. Revision 1.19 2000/04/25 08:42:35 pierre
  804. * New Gabor changes : see fixes.txt
  805. Revision 1.18 2000/04/18 11:42:38 pierre
  806. lot of Gabor changes : see fixes.txt
  807. Revision 1.17 2000/02/07 11:47:25 pierre
  808. * Remove 64Kb limitation for FPC by Gabor
  809. Revision 1.16 2000/01/03 14:59:03 marco
  810. * Fixed Linux code that got time of day. Removed Timezone parameter
  811. Revision 1.15 1999/08/16 18:25:29 peter
  812. * Adjusting the selection when the editor didn't contain any line.
  813. * Reserved word recognition redesigned, but this didn't affect the overall
  814. syntax highlight speed remarkably (at least not on my Amd-K6/350).
  815. The syntax scanner loop is a bit slow but the main problem is the
  816. recognition of special symbols. Switching off symbol processing boosts
  817. the performance up to ca. 200%...
  818. * The editor didn't allow copying (for ex to clipboard) of a single character
  819. * 'File|Save as' caused permanently run-time error 3. Not any more now...
  820. * Compiler Messages window (actually the whole desktop) did not act on any
  821. keypress when compilation failed and thus the window remained visible
  822. + Message windows are now closed upon pressing Esc
  823. + At 'Run' the IDE checks whether any sources are modified, and recompiles
  824. only when neccessary
  825. + BlockRead and BlockWrite (Ctrl+K+R/W) implemented in TCodeEditor
  826. + LineSelect (Ctrl+K+L) implemented
  827. * The IDE had problems closing help windows before saving the desktop
  828. Revision 1.14 1999/07/18 16:26:42 florian
  829. * IDE compiles with for Win32 and basic things are working
  830. Revision 1.13 1999/04/13 10:47:51 daniel
  831. * Fixed for Linux
  832. Revision 1.12 1999/04/07 21:56:00 peter
  833. + object support for browser
  834. * html help fixes
  835. * more desktop saving things
  836. * NODEBUG directive to exclude debugger
  837. Revision 1.11 1999/03/16 12:38:16 peter
  838. * tools macro fixes
  839. + tph writer
  840. + first things for resource files
  841. Revision 1.10 1999/03/08 14:58:19 peter
  842. + prompt with dialogs for tools
  843. Revision 1.9 1999/03/03 16:44:05 pierre
  844. * TPH reader fix from Peter
  845. Revision 1.8 1999/03/01 15:42:11 peter
  846. + Added dummy entries for functions not yet implemented
  847. * MenuBar didn't update itself automatically on command-set changes
  848. * Fixed Debugging/Profiling options dialog
  849. * TCodeEditor converts spaces to tabs at save only if efUseTabChars is
  850. set
  851. * efBackSpaceUnindents works correctly
  852. + 'Messages' window implemented
  853. + Added '$CAP MSG()' and '$CAP EDIT' to available tool-macros
  854. + Added TP message-filter support (for ex. you can call GREP thru
  855. GREP2MSG and view the result in the messages window - just like in TP)
  856. * A 'var' was missing from the param-list of THelpFacility.TopicSearch,
  857. so topic search didn't work...
  858. * In FPHELP.PAS there were still context-variables defined as word instead
  859. of THelpCtx
  860. * StdStatusKeys() was missing from the statusdef for help windows
  861. + Topic-title for index-table can be specified when adding a HTML-files
  862. Revision 1.6 1999/02/20 15:18:35 peter
  863. + ctrl-c capture with confirm dialog
  864. + ascii table in the tools menu
  865. + heapviewer
  866. * empty file fixed
  867. * fixed callback routines in fpdebug to have far for tp7
  868. Revision 1.5 1999/02/19 15:43:22 peter
  869. * compatibility fixes for FV
  870. Revision 1.4 1999/02/18 13:44:37 peter
  871. * search fixed
  872. + backward search
  873. * help fixes
  874. * browser updates
  875. Revision 1.3 1999/02/08 10:37:46 peter
  876. + html helpviewer
  877. Revision 1.2 1998/12/28 15:47:56 peter
  878. + Added user screen support, display & window
  879. + Implemented Editor,Mouse Options dialog
  880. + Added location of .INI and .CFG file
  881. + Option (INI) file managment implemented (see bottom of Options Menu)
  882. + Switches updated
  883. + Run program
  884. Revision 1.4 1998/12/22 10:39:55 peter
  885. + options are now written/read
  886. + find and replace routines
  887. }