whelp.pas 29 KB

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