whelp.pas 29 KB

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