whelp.pas 39 KB

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