whelp.pas 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251
  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. MinFormatVersion = $04; { was $34 }
  25. TP55FormatVersion = $04;
  26. TP70FormatVersion = $34;
  27. Signature = '$*$* &&&&$*$'#0;
  28. ncRawChar = $F;
  29. ncRepChar = $E;
  30. rtFileHeader = Byte ($0);
  31. rtContext = Byte ($1);
  32. rtText = Byte ($2);
  33. rtKeyWord = Byte ($3);
  34. rtIndex = Byte ($4);
  35. rtCompression = Byte ($5);
  36. rtIndexTags = Byte ($6);
  37. ctNone = $00;
  38. ctNibble = $02;
  39. hscLineBreak = #0;
  40. hscLink = #2;
  41. hscLineStart = #3;
  42. hscCode = #5;
  43. hscCenter = #10;
  44. hscRight = #11;
  45. hscNamedMark = #12;
  46. hscTextAttr = #13;
  47. hscTextColor = #14;
  48. hscNormText = #15;
  49. type
  50. FileStamp = array [0..32] of char; {+ null terminator + $1A }
  51. FileSignature = array [0..12] of char; {+ null terminator }
  52. THelpCtx = longint;
  53. THLPVersion = packed record
  54. FormatVersion : byte;
  55. TextVersion : byte;
  56. end;
  57. THLPRecordHeader = packed record
  58. RecType : byte; {TPRecType}
  59. RecLength : word;
  60. end;
  61. THLPContextPos = packed record
  62. LoW: word;
  63. HiB: byte;
  64. end;
  65. THLPContexts = packed record
  66. ContextCount : word;
  67. Contexts : array[0..0] of THLPContextPos;
  68. end;
  69. THLPFileHeader = packed record
  70. Options : word;
  71. MainIndexScreen : word;
  72. MaxScreenSize : word;
  73. Height : byte;
  74. Width : byte;
  75. LeftMargin : byte;
  76. end;
  77. THLPCompression = packed record
  78. CompType : byte;
  79. CharTable : array [0..13] of byte;
  80. end;
  81. THLPIndexDescriptor = packed record
  82. LengthCode : byte;
  83. UniqueChars : array [0..0] of byte;
  84. Context : word;
  85. end;
  86. THLPIndexTable = packed record
  87. IndexCount : word;
  88. Entries : record end;
  89. end;
  90. THLPKeywordDescriptor = packed record
  91. KwContext : word;
  92. end;
  93. THLPKeyWordRecord = packed record
  94. UpContext : word;
  95. DownContext : word;
  96. KeyWordCount : word;
  97. Keywords : array[0..0] of THLPKeywordDescriptor;
  98. end;
  99. THLPKeywordDescriptor55 = packed record
  100. PosY : byte;
  101. StartX : byte;
  102. EndX : byte;
  103. Dunno : array[0..1] of word;
  104. KwContext : word;
  105. end;
  106. THLPKeyWordRecord55 = packed record
  107. UpContext : word;
  108. DownContext : word;
  109. KeyWordCount : byte;
  110. Keywords : array[0..0] of THLPKeywordDescriptor55;
  111. end;
  112. TRecord = packed record
  113. SClass : byte;
  114. Size : word;
  115. Data : pointer;
  116. end;
  117. PIndexEntry = ^TIndexEntry;
  118. TIndexEntry = packed record
  119. Tag : PString;
  120. HelpCtx : THelpCtx;
  121. FileID : word;
  122. end;
  123. PKeywordDescriptor = ^TKeywordDescriptor;
  124. TKeywordDescriptor = packed record
  125. FileID : word;
  126. Context : THelpCtx;
  127. end;
  128. PKeywordDescriptors = ^TKeywordDescriptors;
  129. TKeywordDescriptors = array[0..10900] of TKeywordDescriptor;
  130. PTopic = ^TTopic;
  131. TTopic = object
  132. HelpCtx : THelpCtx;
  133. FileOfs : longint;
  134. TextSize : sw_word;
  135. Text : PByteArray;
  136. LinkCount : sw_word;
  137. Links : PKeywordDescriptors;
  138. LastAccess : longint;
  139. FileID : word;
  140. Param : PString;
  141. StartNamedMark: integer;
  142. NamedMarks : PUnsortedStringCollection;
  143. function LinkSize: sw_word;
  144. function GetNamedMarkIndex(const MarkName: string): sw_integer;
  145. end;
  146. PTopicCollection = ^TTopicCollection;
  147. TTopicCollection = object(TSortedCollection)
  148. function At(Index: sw_Integer): PTopic;
  149. procedure FreeItem(Item: Pointer); virtual;
  150. function Compare(Key1, Key2: Pointer): Sw_Integer; virtual;
  151. function SearchTopic(AHelpCtx: THelpCtx): PTopic;
  152. end;
  153. PIndexEntryCollection = ^TIndexEntryCollection;
  154. TIndexEntryCollection = object(TSortedCollection)
  155. function At(Index: Sw_Integer): PIndexEntry;
  156. procedure FreeItem(Item: Pointer); virtual;
  157. function Compare(Key1, Key2: Pointer): Sw_Integer; virtual;
  158. end;
  159. PHelpFile = ^THelpFile;
  160. THelpFile = object(TObject)
  161. ID : word;
  162. Topics : PTopicCollection;
  163. IndexEntries : PIndexEntryCollection;
  164. constructor Init(AID: word);
  165. function LoadTopic(HelpCtx: THelpCtx): PTopic; virtual;
  166. destructor Done; virtual;
  167. public
  168. function LoadIndex: boolean; virtual;
  169. function SearchTopic(HelpCtx: THelpCtx): PTopic; virtual;
  170. function ReadTopic(T: PTopic): boolean; virtual;
  171. private
  172. procedure MaintainTopicCache;
  173. end;
  174. POAHelpFile = ^TOAHelpFile;
  175. TOAHelpFile = object(THelpFile)
  176. Version : THLPVersion;
  177. Header : THLPFileHeader;
  178. Compression : THLPCompression;
  179. constructor Init(AFileName: string; AID: word);
  180. destructor Done; virtual;
  181. public
  182. function LoadIndex: boolean; virtual;
  183. function ReadTopic(T: PTopic): boolean; virtual;
  184. public { protected }
  185. F: PStream;
  186. TopicsRead : boolean;
  187. IndexTableRead : boolean;
  188. CompressionRead: boolean;
  189. IndexTagsRead : boolean;
  190. IndexTagsPos : longint;
  191. IndexTablePos : longint;
  192. function ReadHeader: boolean;
  193. function ReadTopics: boolean;
  194. function ReadIndexTable: boolean;
  195. function ReadCompression: boolean;
  196. function ReadIndexTags: boolean;
  197. function ReadRecord(var R: TRecord; ReadData: boolean): boolean;
  198. end;
  199. PHelpFileCollection = PCollection;
  200. PHelpFacility = ^THelpFacility;
  201. THelpFacility = object(TObject)
  202. HelpFiles: PHelpFileCollection;
  203. IndexTabSize: sw_integer;
  204. constructor Init;
  205. function AddOAHelpFile(const FileName: string): boolean;
  206. function AddHTMLHelpFile(const FileName, TOCEntry: string): boolean;
  207. function AddHTMLIndexHelpFile(const FileName: string): boolean;
  208. function LoadTopic(SourceFileID: word; Context: THelpCtx): PTopic; virtual;
  209. function TopicSearch(Keyword: string; var FileID: word; var Context: THelpCtx): boolean; virtual;
  210. function BuildIndexTopic: PTopic; virtual;
  211. destructor Done; virtual;
  212. private
  213. LastID: word;
  214. function SearchFile(ID: byte): PHelpFile;
  215. function SearchTopicInHelpFile(F: PHelpFile; Context: THelpCtx): PTopic;
  216. function SearchTopicOwner(SourceFileID: word; Context: THelpCtx): PHelpFile;
  217. function AddFile(H: PHelpFile): boolean;
  218. end;
  219. const TopicCacheSize : sw_integer = 10;
  220. HelpStreamBufSize : sw_integer = 4096;
  221. HelpFacility : PHelpFacility = nil;
  222. MaxHelpTopicSize : sw_word = {$ifdef FPC}3*65520{$else}65520{$endif};
  223. function NewTopic(FileID: byte; HelpCtx: THelpCtx; Pos: longint; Param: string): PTopic;
  224. procedure DisposeTopic(P: PTopic);
  225. function NewIndexEntry(Tag: string; FileID: word; HelpCtx: THelpCtx): PIndexEntry;
  226. procedure DisposeIndexEntry(P: PIndexEntry);
  227. implementation
  228. uses
  229. Dos,
  230. {$ifdef Linux}
  231. linux,
  232. {$endif Linux}
  233. WConsts,WViews,WHTMLHlp;
  234. Function GetDosTicks:longint; { returns ticks at 18.2 Hz, just like DOS }
  235. {$IFDEF LINUX}
  236. var
  237. tv : TimeVal;
  238. tz : TimeZone;
  239. begin
  240. GetTimeOfDay(tv); {Timezone no longer used?}
  241. GetDosTicks:=((tv.Sec mod 86400) div 60)*1092+((tv.Sec mod 60)*1000000+tv.USec) div 54945;
  242. end;
  243. {$endif Linux}
  244. {$ifdef Win32}
  245. begin
  246. GetDosTicks:=(Windows.GetTickCount*5484) div 100;
  247. end;
  248. {$endif Win32}
  249. {$ifdef go32v2}
  250. begin
  251. GetDosTicks:=MemL[$40:$6c];
  252. end;
  253. {$endif go32v2}
  254. {$ifdef TP}
  255. begin
  256. GetDosTicks:=MemL[$40:$6c];
  257. end;
  258. {$endif go32v2}
  259. procedure DisposeRecord(var R: TRecord);
  260. begin
  261. with R do
  262. if (Size>0) and (Data<>nil) then FreeMem(Data, Size);
  263. FillChar(R, SizeOf(R), 0);
  264. end;
  265. function NewTopic(FileID: byte; HelpCtx: THelpCtx; Pos: longint; Param: string): PTopic;
  266. var P: PTopic;
  267. begin
  268. New(P); FillChar(P^,SizeOf(P^), 0);
  269. P^.HelpCtx:=HelpCtx; P^.FileOfs:=Pos; P^.FileID:=FileID;
  270. P^.Param:=NewStr(Param);
  271. New(P^.NamedMarks, Init(100,100));
  272. NewTopic:=P;
  273. end;
  274. procedure DisposeTopic(P: PTopic);
  275. begin
  276. if P<>nil then
  277. begin
  278. if (P^.TextSize>0) and (P^.Text<>nil) then
  279. FreeMem(P^.Text,P^.TextSize);
  280. P^.Text:=nil;
  281. if (P^.LinkCount>0) and (P^.Links<>nil) then
  282. FreeMem(P^.Links,P^.LinkSize);
  283. P^.Links:=nil;
  284. if P^.Param<>nil then DisposeStr(P^.Param); P^.Param:=nil;
  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); Move(T^,NT^,SizeOf(NT^));
  297. if NT^.Text<>nil then
  298. begin GetMem(NT^.Text,NT^.TextSize); Move(T^.Text^,NT^.Text^,NT^.TextSize); end;
  299. if NT^.Links<>nil then
  300. begin GetMem(NT^.Links,NT^.LinkSize); Move(T^.Links^,NT^.Links^,NT^.LinkSize); end;
  301. if NT^.Param<>nil then
  302. NT^.Param:=NewStr(T^.Param^);
  303. if Assigned(T^.NamedMarks) then
  304. begin
  305. New(NT^.NamedMarks, Init(T^.NamedMarks^.Count,10));
  306. T^.NamedMarks^.ForEach(@CloneMark);
  307. end;
  308. CloneTopic:=NT;
  309. end;
  310. function NewIndexEntry(Tag: string; FileID: word; HelpCtx: THelpCtx): PIndexEntry;
  311. var P: PIndexEntry;
  312. begin
  313. New(P); FillChar(P^,SizeOf(P^), 0);
  314. P^.Tag:=NewStr(Tag); P^.FileID:=FileID; P^.HelpCtx:=HelpCtx;
  315. NewIndexEntry:=P;
  316. end;
  317. procedure DisposeIndexEntry(P: PIndexEntry);
  318. begin
  319. if P<>nil then
  320. begin
  321. if P^.Tag<>nil then DisposeStr(P^.Tag);
  322. Dispose(P);
  323. end;
  324. end;
  325. function TTopic.LinkSize: sw_word;
  326. begin
  327. LinkSize:=LinkCount*SizeOf(Links^[0]);
  328. end;
  329. function TTopic.GetNamedMarkIndex(const MarkName: string): sw_integer;
  330. var I,Index: sw_integer;
  331. begin
  332. Index:=-1;
  333. if Assigned(NamedMarks) then
  334. for I:=0 to NamedMarks^.Count-1 do
  335. if CompareText(GetStr(NamedMarks^.At(I)),MarkName)=0 then
  336. begin
  337. Index:=I;
  338. Break;
  339. end;
  340. GetNamedMarkIndex:=Index;
  341. end;
  342. function TTopicCollection.At(Index: sw_Integer): PTopic;
  343. begin
  344. At:=inherited At(Index);
  345. end;
  346. procedure TTopicCollection.FreeItem(Item: Pointer);
  347. begin
  348. if Item<>nil then DisposeTopic(Item);
  349. end;
  350. function TTopicCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
  351. var K1: PTopic absolute Key1;
  352. K2: PTopic absolute Key2;
  353. R: Sw_integer;
  354. begin
  355. if K1^.HelpCtx<K2^.HelpCtx then R:=-1 else
  356. if K1^.HelpCtx>K2^.HelpCtx then R:= 1 else
  357. R:=0;
  358. Compare:=R;
  359. end;
  360. function TTopicCollection.SearchTopic(AHelpCtx: THelpCtx): PTopic;
  361. var T: TTopic;
  362. P: PTopic;
  363. Index: sw_integer;
  364. begin
  365. T.HelpCtx:=AHelpCtx;
  366. if Search(@T,Index) then
  367. P:=At(Index)
  368. else
  369. P:=nil;
  370. SearchTopic:=P;
  371. end;
  372. function TIndexEntryCollection.At(Index: Sw_Integer): PIndexEntry;
  373. begin
  374. At:=inherited At(Index);
  375. end;
  376. procedure TIndexEntryCollection.FreeItem(Item: Pointer);
  377. begin
  378. if Item<>nil then DisposeIndexEntry(Item);
  379. end;
  380. function TIndexEntryCollection.Compare(Key1, Key2: Pointer): Sw_Integer;
  381. var K1: PIndexEntry absolute Key1;
  382. K2: PIndexEntry absolute Key2;
  383. R: Sw_integer;
  384. S1,S2: string;
  385. begin
  386. S1:=UpcaseStr(K1^.Tag^); S2:=UpcaseStr(K2^.Tag^);
  387. if S1<S2 then R:=-1 else
  388. if S1>S2 then R:=1 else
  389. R:=0;
  390. Compare:=R;
  391. end;
  392. constructor THelpFile.Init(AID: word);
  393. begin
  394. inherited Init;
  395. ID:=AID;
  396. New(Topics, Init(500,500));
  397. New(IndexEntries, Init(200,100));
  398. end;
  399. function THelpFile.LoadTopic(HelpCtx: THelpCtx): PTopic;
  400. var T: PTopic;
  401. begin
  402. T:=SearchTopic(HelpCtx);
  403. if (T<>nil) then
  404. if T^.Text=nil then
  405. begin
  406. MaintainTopicCache;
  407. if ReadTopic(T)=false then
  408. T:=nil;
  409. if (T<>nil) and (T^.Text=nil) then T:=nil;
  410. end;
  411. if T<>nil then
  412. begin T^.LastAccess:=GetDosTicks; T:=CloneTopic(T); end;
  413. LoadTopic:=T;
  414. end;
  415. function THelpFile.LoadIndex: boolean;
  416. begin
  417. Abstract;
  418. LoadIndex:=false; { remove warning }
  419. end;
  420. function THelpFile.SearchTopic(HelpCtx: THelpCtx): PTopic;
  421. var T: PTopic;
  422. begin
  423. T:=Topics^.SearchTopic(HelpCtx);
  424. SearchTopic:=T;
  425. end;
  426. function THelpFile.ReadTopic(T: PTopic): boolean;
  427. begin
  428. Abstract;
  429. ReadTopic:=false; { remove warning }
  430. end;
  431. procedure THelpFile.MaintainTopicCache;
  432. var Count: sw_integer;
  433. MinLRU: longint;
  434. procedure CountThem(P: PTopic); {$ifndef FPC}far;{$endif}
  435. begin if (P^.Text<>nil) or (P^.Links<>nil) then Inc(Count); end;
  436. procedure SearchLRU(P: PTopic); {$ifndef FPC}far;{$endif}
  437. begin if P^.LastAccess<MinLRU then begin MinLRU:=P^.LastAccess; end; end;
  438. var P: PTopic;
  439. begin
  440. Count:=0; Topics^.ForEach(@CountThem);
  441. if (Count>=TopicCacheSize) then
  442. begin
  443. MinLRU:=MaxLongint; P:=nil; Topics^.ForEach(@SearchLRU);
  444. if P<>nil then
  445. begin
  446. FreeMem(P^.Text,P^.TextSize); P^.TextSize:=0; P^.Text:=nil;
  447. FreeMem(P^.Links,P^.LinkSize); P^.LinkCount:=0; P^.Links:=nil;
  448. end;
  449. end;
  450. end;
  451. destructor THelpFile.Done;
  452. begin
  453. if Topics<>nil then Dispose(Topics, Done);
  454. if IndexEntries<>nil then Dispose(IndexEntries, Done);
  455. inherited Done;
  456. end;
  457. constructor TOAHelpFile.Init(AFileName: string; AID: word);
  458. var OK: boolean;
  459. FS,L: longint;
  460. R: TRecord;
  461. begin
  462. inherited Init(AID);
  463. F:=New(PBufStream, Init(AFileName, stOpenRead, HelpStreamBufSize));
  464. OK:=F<>nil;
  465. if OK then OK:=(F^.Status=stOK);
  466. if OK then
  467. begin
  468. FS:=F^.GetSize;
  469. OK:=ReadHeader;
  470. end;
  471. while OK do
  472. begin
  473. L:=F^.GetPos;
  474. if (L>=FS) then Break;
  475. OK:=ReadRecord(R,false);
  476. if (OK=false) or (R.SClass=0) or (R.Size=0) then Break;
  477. case R.SClass of
  478. rtContext : begin F^.Seek(L); OK:=ReadTopics; end;
  479. rtText : {Skip};
  480. rtKeyword : {Skip};
  481. rtIndex : begin IndexTablePos:=L; {OK:=ReadIndexTable; }end;
  482. rtCompression : begin F^.Seek(L); OK:=ReadCompression; end;
  483. rtIndexTags : begin IndexTagsPos:=L; {OK:=ReadIndexTags; }end;
  484. else
  485. begin
  486. {$ifdef DEBUGMSG}
  487. ClearFormatParams;
  488. AddFormatParamInt(R.SClass);
  489. AddFormatParamInt(L);
  490. AddFormatParamInt(R.Size);
  491. ErrorBox('Uknown help record tag %x encountered, '+
  492. 'offset %x, size %d',@FormatParams);
  493. {$else}
  494. {Skip};
  495. {$endif}
  496. end;
  497. end;
  498. if OK then
  499. begin Inc(L, SizeOf(THLPRecordHeader)); Inc(L, R.Size); F^.Seek(L); OK:=(F^.Status=stOK); end
  500. end;
  501. OK:=OK and (TopicsRead=true);
  502. if OK=false then Fail;
  503. end;
  504. function TOAHelpFile.LoadIndex: boolean;
  505. begin
  506. LoadIndex:=ReadIndexTable;
  507. end;
  508. function TOAHelpFile.ReadHeader: boolean;
  509. var S: string;
  510. P: longint;
  511. R: TRecord;
  512. OK: boolean;
  513. begin
  514. F^.Seek(0);
  515. F^.Read(S[1],128); S[0]:=#255;
  516. OK:=(F^.Status=stOK); P:=Pos(Signature,S);
  517. OK:=OK and (P>0);
  518. if OK then
  519. begin
  520. F^.Seek(P+length(Signature)-1);
  521. F^.Read(Version,SizeOf(Version));
  522. OK:=(F^.Status=stOK) and (Version.FormatVersion>=MinFormatVersion);
  523. if OK then
  524. begin
  525. OK:=ReadRecord(R,true);
  526. OK:=OK and (R.SClass=rtFileHeader) and (R.Size=SizeOf(Header));
  527. if OK then Move(R.Data^,Header,SizeOf(Header));
  528. DisposeRecord(R);
  529. end;
  530. end;
  531. ReadHeader:=OK;
  532. end;
  533. function TOAHelpFile.ReadTopics: boolean;
  534. var OK: boolean;
  535. R: TRecord;
  536. L,I: longint;
  537. function GetCtxPos(C: THLPContextPos): longint;
  538. begin
  539. GetCtxPos:=longint(C.HiB) shl 16 + C.LoW;
  540. end;
  541. begin
  542. OK:=ReadRecord(R, true);
  543. if OK then
  544. with THLPContexts(R.Data^) do
  545. for I:=1 to longint(ContextCount)-1 do
  546. begin
  547. if Topics^.Count=MaxCollectionSize then Break;
  548. L:=GetCtxPos(Contexts[I]);
  549. if (L and $800000)<>0 then L:=not L;
  550. if (L=-1) and (Header.MainIndexScreen>0) then
  551. L:=GetCtxPos(Contexts[Header.MainIndexScreen]);
  552. if (L>0) then
  553. Topics^.Insert(NewTopic(ID,I,L,''));
  554. end;
  555. DisposeRecord(R);
  556. TopicsRead:=OK;
  557. ReadTopics:=OK;
  558. end;
  559. function TOAHelpFile.ReadIndexTable: boolean;
  560. var OK: boolean;
  561. R: TRecord;
  562. I: longint;
  563. LastTag,S: string;
  564. CurPtr: sw_word;
  565. HelpCtx: THelpCtx;
  566. LenCode,CopyCnt,AddLen: byte;
  567. type pword = ^word;
  568. begin
  569. if IndexTableRead then OK:=true else
  570. begin
  571. LastTag:=''; CurPtr:=0;
  572. OK:=(IndexTablePos<>0);
  573. if OK then begin F^.Seek(IndexTablePos); OK:=F^.Status=stOK; end;
  574. if OK then OK:=ReadRecord(R, true);
  575. if OK then
  576. with THLPIndexTable(R.Data^) do
  577. for I:=0 to IndexCount-1 do
  578. begin
  579. LenCode:=PByteArray(@Entries)^[CurPtr];
  580. AddLen:=LenCode and $1f; CopyCnt:=LenCode shr 5;
  581. S[0]:=chr(AddLen); Move(PByteArray(@Entries)^[CurPtr+1],S[1],AddLen);
  582. LastTag:=copy(LastTag,1,CopyCnt)+S;
  583. HelpCtx:=PWord(@PByteArray(@Entries)^[CurPtr+1+AddLen])^;
  584. IndexEntries^.Insert(NewIndexEntry(LastTag,ID,HelpCtx));
  585. Inc(CurPtr,1+AddLen+2);
  586. end;
  587. DisposeRecord(R);
  588. IndexTableRead:=OK;
  589. end;
  590. ReadIndexTable:=OK;
  591. end;
  592. function TOAHelpFile.ReadCompression: boolean;
  593. var OK: boolean;
  594. R: TRecord;
  595. begin
  596. OK:=ReadRecord(R, true);
  597. OK:=OK and (R.Size=SizeOf(THLPCompression));
  598. if OK then Move(R.Data^,Compression,SizeOf(Compression));
  599. DisposeRecord(R);
  600. CompressionRead:=OK;
  601. ReadCompression:=OK;
  602. end;
  603. function TOAHelpFile.ReadIndexTags: boolean;
  604. var OK: boolean;
  605. begin
  606. OK:={ReadRecord(R, true)}true;
  607. IndexTagsRead:=OK;
  608. ReadIndexTags:=OK;
  609. end;
  610. function TOAHelpFile.ReadRecord(var R: TRecord; ReadData: boolean): boolean;
  611. var OK: boolean;
  612. H: THLPRecordHeader;
  613. begin
  614. FillChar(R, SizeOf(R), 0);
  615. F^.Read(H,SizeOf(H));
  616. OK:=F^.Status=stOK;
  617. if OK then
  618. begin
  619. R.SClass:=H.RecType; R.Size:=H.RecLength;
  620. if (R.Size>0) and ReadData then
  621. begin
  622. GetMem(R.Data,R.Size);
  623. F^.Read(R.Data^,R.Size);
  624. OK:=F^.Status=stOK;
  625. end;
  626. if OK=false then DisposeRecord(R);
  627. end;
  628. ReadRecord:=OK;
  629. end;
  630. function TOAHelpFile.ReadTopic(T: PTopic): boolean;
  631. var SrcPtr,DestPtr,TopicSize: sw_word;
  632. NewR: TRecord;
  633. LinkPosCount: integer;
  634. LinkPos: array[1..50] of TRect;
  635. function IsLinkPosStart(X,Y: integer): boolean;
  636. var OK: boolean;
  637. I: integer;
  638. begin
  639. OK:=false;
  640. for I:=1 to LinkPosCount do
  641. with LinkPos[I] do
  642. if (A.X=X) and (A.Y=Y) then
  643. begin
  644. OK:=true;
  645. Break;
  646. end;
  647. IsLinkPosStart:=OK;
  648. end;
  649. function IsLinkPosEnd(X,Y: integer): boolean;
  650. var OK: boolean;
  651. I: integer;
  652. begin
  653. OK:=false;
  654. for I:=1 to LinkPosCount do
  655. with LinkPos[I] do
  656. if (B.X=X) and (B.Y=Y) then
  657. begin
  658. OK:=true;
  659. Break;
  660. end;
  661. IsLinkPosEnd:=OK;
  662. end;
  663. function ExtractTextRec(var R: TRecord): boolean;
  664. function GetNextNibble: byte;
  665. var B,N: byte;
  666. begin
  667. B:=PByteArray(R.Data)^[SrcPtr div 2];
  668. N:=( B and ($0f shl (4*(SrcPtr mod 2))) ) shr (4*(SrcPtr mod 2));
  669. Inc(SrcPtr);
  670. GetNextNibble:=N;
  671. end;
  672. procedure RealAddChar(C: char);
  673. begin
  674. if Assigned(NewR.Data) then
  675. PByteArray(NewR.Data)^[DestPtr]:=ord(C);
  676. Inc(DestPtr);
  677. end;
  678. var CurX,CurY: integer;
  679. InLink: boolean;
  680. procedure AddChar(C: char);
  681. begin
  682. if IsLinkPosStart(CurX+2,CurY) then
  683. begin
  684. RealAddChar(hscLink);
  685. InLink:=true;
  686. end
  687. else
  688. if (C=hscLineBreak) and (InLink) then
  689. begin
  690. RealAddChar(hscLink);
  691. InLink:=false;
  692. end;
  693. RealAddChar(C);
  694. if IsLinkPosEnd(CurX+2,CurY) then
  695. begin
  696. RealAddChar(hscLink);
  697. InLink:=false;
  698. end;
  699. if C<>hscLineBreak then
  700. Inc(CurX)
  701. else
  702. begin
  703. CurX:=0;
  704. Inc(CurY);
  705. end;
  706. end;
  707. var OK: boolean;
  708. C: char;
  709. P: pointer;
  710. function GetNextChar: char;
  711. var C: char;
  712. I,N,Cnt: byte;
  713. begin
  714. N:=GetNextNibble;
  715. case N of
  716. $00 : C:=#0;
  717. $01..$0D : C:=chr(Compression.CharTable[N]);
  718. ncRawChar : begin
  719. I:=GetNextNibble;
  720. C:=chr(I+GetNextNibble shl 4);
  721. end;
  722. ncRepChar : begin
  723. Cnt:=2+GetNextNibble;
  724. C:=GetNextChar{$ifdef FPC}(){$endif};
  725. for I:=1 to Cnt-1 do AddChar(C);
  726. end;
  727. end;
  728. GetNextChar:=C;
  729. end;
  730. begin
  731. OK:=Compression.CompType in[ctNone,ctNibble];
  732. if OK then
  733. case Compression.CompType of
  734. ctNone : ;
  735. ctNibble :
  736. begin
  737. CurX:=0; CurY:=0; InLink:=false;
  738. NewR.SClass:=0;
  739. NewR.Size:=0;
  740. NewR.Data:=nil;
  741. SrcPtr:=0; DestPtr:=0;
  742. while SrcPtr<(R.Size*2) do
  743. begin
  744. C:=GetNextChar;
  745. AddChar(C);
  746. end;
  747. if InLink then AddChar(hscLineBreak);
  748. TopicSize:=DestPtr;
  749. CurX:=0; CurY:=0; InLink:=false;
  750. NewR.SClass:=R.SClass;
  751. NewR.Size:=Min(MaxHelpTopicSize,TopicSize);
  752. GetMem(NewR.Data, NewR.Size);
  753. SrcPtr:=0; DestPtr:=0;
  754. while SrcPtr<(R.Size*2) do
  755. begin
  756. C:=GetNextChar;
  757. AddChar(C);
  758. end;
  759. if InLink then AddChar(hscLineBreak);
  760. DisposeRecord(R); R:=NewR;
  761. if (R.Size>DestPtr) then
  762. begin
  763. P:=R.Data; GetMem(R.Data,DestPtr);
  764. Move(P^,R.Data^,DestPtr); FreeMem(P,R.Size); R.Size:=DestPtr;
  765. end;
  766. end;
  767. else OK:=false;
  768. end;
  769. ExtractTextRec:=OK;
  770. end;
  771. var OK: boolean;
  772. TextR,KeyWR: TRecord;
  773. I: sw_word;
  774. begin
  775. OK:=T<>nil;
  776. if OK and (T^.Text=nil) then
  777. begin
  778. LinkPosCount:=0; FillChar(LinkPos,Sizeof(LinkPos),0);
  779. FillChar(TextR,SizeOf(TextR),0); FillChar(KeyWR,SizeOf(KeyWR),0);
  780. F^.Seek(T^.FileOfs); OK:=F^.Status=stOK;
  781. if OK then OK:=ReadRecord(TextR,true);
  782. OK:=OK and (TextR.SClass=rtText);
  783. if OK then OK:=ReadRecord(KeyWR,true);
  784. OK:=OK and (KeyWR.SClass=rtKeyword);
  785. if OK then
  786. begin
  787. case Version.FormatVersion of
  788. TP55FormatVersion :
  789. with THLPKeywordRecord55(KeyWR.Data^) do
  790. begin
  791. T^.LinkCount:=KeywordCount;
  792. GetMem(T^.Links,T^.LinkSize);
  793. if T^.LinkCount>0 then
  794. for I:=0 to T^.LinkCount-1 do
  795. with Keywords[I] do
  796. begin
  797. T^.Links^[I].Context:=KwContext;
  798. T^.Links^[I].FileID:=ID;
  799. Inc(LinkPosCount);
  800. with LinkPos[LinkPosCount] do
  801. begin
  802. A.Y:=PosY-1; B.Y:=PosY-1;
  803. A.X:=StartX-1; B.X:=EndX-1;
  804. end;
  805. end;
  806. end;
  807. else
  808. with THLPKeywordRecord(KeyWR.Data^) do
  809. begin
  810. T^.LinkCount:=KeywordCount;
  811. GetMem(T^.Links,T^.LinkSize);
  812. if KeywordCount>0 then
  813. for I:=0 to KeywordCount-1 do
  814. begin
  815. T^.Links^[I].Context:=Keywords[I].KwContext;
  816. T^.Links^[I].FileID:=ID;
  817. end;
  818. end;
  819. end;
  820. end;
  821. if OK then OK:=ExtractTextRec(TextR);
  822. if OK then
  823. if TextR.Size>0 then
  824. begin
  825. T^.Text:=TextR.Data; T^.TextSize:=TextR.Size;
  826. TextR.Data:=nil; TextR.Size:=0;
  827. end;
  828. DisposeRecord(TextR); DisposeRecord(KeyWR);
  829. end;
  830. ReadTopic:=OK;
  831. end;
  832. destructor TOAHelpFile.Done;
  833. begin
  834. if F<>nil then Dispose(F, Done);
  835. inherited Done;
  836. end;
  837. constructor THelpFacility.Init;
  838. begin
  839. inherited Init;
  840. New(HelpFiles, Init(10,10));
  841. IndexTabSize:=40;
  842. end;
  843. function THelpFacility.AddOAHelpFile(const FileName: string): boolean;
  844. var H: PHelpFile;
  845. begin
  846. H:=New(POAHelpFile, Init(FileName, LastID+1));
  847. AddOAHelpFile:=AddFile(H);
  848. end;
  849. function THelpFacility.AddHTMLHelpFile(const FileName, TOCEntry: string): boolean;
  850. var H: PHelpFile;
  851. begin
  852. H:=New(PHTMLHelpFile, Init(FileName, LastID+1, TOCEntry));
  853. AddHTMLHelpFile:=AddFile(H);;
  854. end;
  855. function THelpFacility.AddHTMLIndexHelpFile(const FileName: string): boolean;
  856. var H: PHelpFile;
  857. begin
  858. H:=New(PHTMLIndexHelpFile, Init(FileName, LastID+1));
  859. AddHTMLIndexHelpFile:=AddFile(H);;
  860. end;
  861. function THelpFacility.AddFile(H: PHelpFile): boolean;
  862. begin
  863. if H<>nil then
  864. begin
  865. HelpFiles^.Insert(H);
  866. Inc(LastID);
  867. end;
  868. AddFile:=H<>nil;
  869. end;
  870. function THelpFacility.SearchTopicOwner(SourceFileID: word; Context: THelpCtx): PHelpFile;
  871. var P: PTopic;
  872. HelpFile: PHelpFile;
  873. function Search(F: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  874. begin
  875. P:=SearchTopicInHelpFile(F,Context); if P<>nil then HelpFile:=F;
  876. Search:=P<>nil;
  877. end;
  878. begin
  879. HelpFile:=nil;
  880. if SourceFileID=0 then P:=nil else
  881. begin
  882. HelpFile:=SearchFile(SourceFileID);
  883. P:=SearchTopicInHelpFile(HelpFile,Context);
  884. end;
  885. if P=nil then HelpFiles^.FirstThat(@Search);
  886. if P=nil then HelpFile:=nil;
  887. SearchTopicOwner:=HelpFile;
  888. end;
  889. function THelpFacility.LoadTopic(SourceFileID: word; Context: THelpCtx): PTopic;
  890. var P: PTopic;
  891. H: PHelpFile;
  892. begin
  893. if (SourceFileID=0) and (Context=0) then
  894. P:=BuildIndexTopic else
  895. begin
  896. H:=SearchTopicOwner(SourceFileID,Context);
  897. if (H=nil) then P:=nil else
  898. P:=H^.LoadTopic(Context);
  899. end;
  900. LoadTopic:=P;
  901. end;
  902. function THelpFacility.TopicSearch(Keyword: string; var FileID: word; var Context: THelpCtx): boolean;
  903. function ScanHelpFile(H: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  904. function Search(P: PIndexEntry): boolean; {$ifndef FPC}far;{$endif}
  905. begin
  906. Search:=copy(UpcaseStr(P^.Tag^),1,length(Keyword))=Keyword;
  907. end;
  908. var P: PIndexEntry;
  909. begin
  910. H^.LoadIndex;
  911. P:=H^.IndexEntries^.FirstThat(@Search);
  912. if P<>nil then begin FileID:=H^.ID; Context:=P^.HelpCtx; end;
  913. ScanHelpFile:=P<>nil;
  914. end;
  915. begin
  916. Keyword:=UpcaseStr(Keyword);
  917. TopicSearch:=HelpFiles^.FirstThat(@ScanHelpFile)<>nil;
  918. end;
  919. function THelpFacility.BuildIndexTopic: PTopic;
  920. var T: PTopic;
  921. Keywords: PIndexEntryCollection;
  922. Lines: PUnsortedStringCollection;
  923. procedure InsertKeywordsOfFile(H: PHelpFile); {$ifndef FPC}far;{$endif}
  924. function InsertKeywords(P: PIndexEntry): boolean; {$ifndef FPC}far;{$endif}
  925. begin
  926. Keywords^.Insert(P);
  927. InsertKeywords:=Keywords^.Count>=MaxCollectionSize;
  928. end;
  929. begin
  930. H^.LoadIndex;
  931. if Keywords^.Count<MaxCollectionSize then
  932. H^.IndexEntries^.FirstThat(@InsertKeywords);
  933. end;
  934. procedure AddLine(S: string);
  935. begin
  936. if S='' then S:=' ';
  937. Lines^.Insert(NewStr(S));
  938. end;
  939. procedure RenderTopic;
  940. var Size,CurPtr,I: sw_word;
  941. S: string;
  942. function CountSize(P: PString): boolean; {$ifndef FPC}far;{$endif}
  943. begin Inc(Size, length(P^)+1); CountSize:=Size>MaxHelpTopicSize-300; end;
  944. begin
  945. Size:=0; Lines^.FirstThat(@CountSize);
  946. T^.TextSize:=Size; GetMem(T^.Text,T^.TextSize);
  947. CurPtr:=0;
  948. for I:=0 to Lines^.Count-1 do
  949. begin
  950. S:=Lines^.At(I)^;
  951. Size:=length(S)+1; S[Size]:=hscLineBreak;
  952. Move(S[1],PByteArray(T^.Text)^[CurPtr],Size);
  953. Inc(CurPtr,Size);
  954. if CurPtr>=T^.TextSize then Break;
  955. end;
  956. end;
  957. var Line: string;
  958. procedure FlushLine;
  959. begin
  960. if Line<>'' then AddLine(Line); Line:='';
  961. end;
  962. var KWCount,NLFlag: sw_integer;
  963. LastFirstChar: char;
  964. procedure NewSection(FirstChar: char);
  965. begin
  966. if FirstChar<=#64 then FirstChar:=#32;
  967. FlushLine;
  968. AddLine('');
  969. AddLine(FirstChar);
  970. AddLine('');
  971. LastFirstChar:=FirstChar;
  972. NLFlag:=0;
  973. end;
  974. procedure AddKeyword(KWS: string);
  975. begin
  976. Inc(KWCount); if KWCount=1 then NLFlag:=0;
  977. if (KWCount=1) or
  978. ( (Upcase(KWS[1])<>LastFirstChar) and ( (LastFirstChar>#64) or (KWS[1]>#64) ) ) then
  979. NewSection(Upcase(KWS[1]));
  980. if (NLFlag mod 2)=0
  981. then Line:=' '+#2+KWS+#2
  982. else begin
  983. Line:=RExpand(Line,IndexTabSize)+#2+KWS+#2;
  984. FlushLine;
  985. end;
  986. Inc(NLFlag);
  987. end;
  988. var KW: PIndexEntry;
  989. I: sw_integer;
  990. begin
  991. New(Keywords, Init(5000,1000));
  992. HelpFiles^.ForEach(@InsertKeywordsOfFile);
  993. New(Lines, Init((Keywords^.Count div 2)+100,100));
  994. T:=NewTopic(0,0,0,'');
  995. if HelpFiles^.Count=0 then
  996. begin
  997. AddLine('');
  998. AddLine(' '+msg_nohelpfilesinstalled)
  999. end else
  1000. begin
  1001. AddLine(' '+msg_helpindex);
  1002. KWCount:=0; Line:='';
  1003. T^.LinkCount:=Keywords^.Count;
  1004. GetMem(T^.Links,T^.LinkSize);
  1005. for I:=0 to Keywords^.Count-1 do
  1006. begin
  1007. KW:=Keywords^.At(I);
  1008. AddKeyword(KW^.Tag^);
  1009. T^.Links^[I].Context:=longint(KW^.HelpCtx); T^.Links^[I].FileID:=KW^.FileID;
  1010. end;
  1011. FlushLine;
  1012. AddLine('');
  1013. end;
  1014. RenderTopic;
  1015. Dispose(Lines, Done);
  1016. Keywords^.DeleteAll; Dispose(Keywords, Done);
  1017. BuildIndexTopic:=T;
  1018. end;
  1019. function THelpFacility.SearchFile(ID: byte): PHelpFile;
  1020. function Match(P: PHelpFile): boolean; {$ifndef FPC}far;{$endif}
  1021. begin
  1022. Match:=(P^.ID=ID);
  1023. end;
  1024. begin
  1025. SearchFile:=HelpFiles^.FirstThat(@Match);
  1026. end;
  1027. function THelpFacility.SearchTopicInHelpFile(F: PHelpFile; Context: THelpCtx): PTopic;
  1028. var P: PTopic;
  1029. begin
  1030. if F=nil then P:=nil else
  1031. P:=F^.SearchTopic(Context);
  1032. SearchTopicInHelpFile:=P;
  1033. end;
  1034. destructor THelpFacility.Done;
  1035. begin
  1036. inherited Done;
  1037. Dispose(HelpFiles, Done);
  1038. end;
  1039. END.
  1040. {
  1041. $Log$
  1042. Revision 1.23 2000-06-16 08:50:44 pierre
  1043. + new bunch of Gabor's changes
  1044. Revision 1.22 2000/05/31 20:42:02 pierre
  1045. * fixthe TRect problem by 'using' windows before objects
  1046. Revision 1.21 2000/05/30 07:18:33 pierre
  1047. + colors for HTML help by Gabor
  1048. Revision 1.20 2000/05/29 10:44:59 pierre
  1049. + New bunch of Gabor's changes: see fixes.txt
  1050. Revision 1.19 2000/04/25 08:42:35 pierre
  1051. * New Gabor changes : see fixes.txt
  1052. Revision 1.18 2000/04/18 11:42:38 pierre
  1053. lot of Gabor changes : see fixes.txt
  1054. Revision 1.17 2000/02/07 11:47:25 pierre
  1055. * Remove 64Kb limitation for FPC by Gabor
  1056. Revision 1.16 2000/01/03 14:59:03 marco
  1057. * Fixed Linux code that got time of day. Removed Timezone parameter
  1058. Revision 1.15 1999/08/16 18:25:29 peter
  1059. * Adjusting the selection when the editor didn't contain any line.
  1060. * Reserved word recognition redesigned, but this didn't affect the overall
  1061. syntax highlight speed remarkably (at least not on my Amd-K6/350).
  1062. The syntax scanner loop is a bit slow but the main problem is the
  1063. recognition of special symbols. Switching off symbol processing boosts
  1064. the performance up to ca. 200%...
  1065. * The editor didn't allow copying (for ex to clipboard) of a single character
  1066. * 'File|Save as' caused permanently run-time error 3. Not any more now...
  1067. * Compiler Messages window (actually the whole desktop) did not act on any
  1068. keypress when compilation failed and thus the window remained visible
  1069. + Message windows are now closed upon pressing Esc
  1070. + At 'Run' the IDE checks whether any sources are modified, and recompiles
  1071. only when neccessary
  1072. + BlockRead and BlockWrite (Ctrl+K+R/W) implemented in TCodeEditor
  1073. + LineSelect (Ctrl+K+L) implemented
  1074. * The IDE had problems closing help windows before saving the desktop
  1075. Revision 1.14 1999/07/18 16:26:42 florian
  1076. * IDE compiles with for Win32 and basic things are working
  1077. Revision 1.13 1999/04/13 10:47:51 daniel
  1078. * Fixed for Linux
  1079. Revision 1.12 1999/04/07 21:56:00 peter
  1080. + object support for browser
  1081. * html help fixes
  1082. * more desktop saving things
  1083. * NODEBUG directive to exclude debugger
  1084. Revision 1.11 1999/03/16 12:38:16 peter
  1085. * tools macro fixes
  1086. + tph writer
  1087. + first things for resource files
  1088. Revision 1.10 1999/03/08 14:58:19 peter
  1089. + prompt with dialogs for tools
  1090. Revision 1.9 1999/03/03 16:44:05 pierre
  1091. * TPH reader fix from Peter
  1092. Revision 1.8 1999/03/01 15:42:11 peter
  1093. + Added dummy entries for functions not yet implemented
  1094. * MenuBar didn't update itself automatically on command-set changes
  1095. * Fixed Debugging/Profiling options dialog
  1096. * TCodeEditor converts spaces to tabs at save only if efUseTabChars is
  1097. set
  1098. * efBackSpaceUnindents works correctly
  1099. + 'Messages' window implemented
  1100. + Added '$CAP MSG()' and '$CAP EDIT' to available tool-macros
  1101. + Added TP message-filter support (for ex. you can call GREP thru
  1102. GREP2MSG and view the result in the messages window - just like in TP)
  1103. * A 'var' was missing from the param-list of THelpFacility.TopicSearch,
  1104. so topic search didn't work...
  1105. * In FPHELP.PAS there were still context-variables defined as word instead
  1106. of THelpCtx
  1107. * StdStatusKeys() was missing from the statusdef for help windows
  1108. + Topic-title for index-table can be specified when adding a HTML-files
  1109. Revision 1.6 1999/02/20 15:18:35 peter
  1110. + ctrl-c capture with confirm dialog
  1111. + ascii table in the tools menu
  1112. + heapviewer
  1113. * empty file fixed
  1114. * fixed callback routines in fpdebug to have far for tp7
  1115. Revision 1.5 1999/02/19 15:43:22 peter
  1116. * compatibility fixes for FV
  1117. Revision 1.4 1999/02/18 13:44:37 peter
  1118. * search fixed
  1119. + backward search
  1120. * help fixes
  1121. * browser updates
  1122. Revision 1.3 1999/02/08 10:37:46 peter
  1123. + html helpviewer
  1124. Revision 1.2 1998/12/28 15:47:56 peter
  1125. + Added user screen support, display & window
  1126. + Implemented Editor,Mouse Options dialog
  1127. + Added location of .INI and .CFG file
  1128. + Option (INI) file managment implemented (see bottom of Options Menu)
  1129. + Switches updated
  1130. + Run program
  1131. Revision 1.4 1998/12/22 10:39:55 peter
  1132. + options are now written/read
  1133. + find and replace routines
  1134. }