whelp.pas 39 KB

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