whtmlhlp.pas 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. unit WHTMLHlp;
  2. interface
  3. uses Objects,WHTML,WHelp;
  4. const
  5. ListIndent = 2;
  6. DefIndent = 4;
  7. MaxTopicLinks = 100;
  8. type
  9. PTopicLinkCollection = ^TTopicLinkCollection;
  10. TTopicLinkCollection = object(TStringCollection)
  11. procedure Insert(Item: Pointer); virtual;
  12. function At(Index: sw_Integer): PString;
  13. function AddItem(Item: string): integer;
  14. end;
  15. TParagraphAlign = (paLeft,paCenter,paRight);
  16. PHTMLTopicRenderer = ^THTMLTopicRenderer;
  17. THTMLTopicRenderer = object(THTMLParser)
  18. function BuildTopic(P: PTopic; HTMLFile: PTextFile; ATopicLinks: PTopicLinkCollection): boolean;
  19. public
  20. procedure DocAddTextChar(C: char); virtual;
  21. procedure DocSoftBreak; virtual;
  22. procedure DocTYPE; virtual;
  23. procedure DocHTML(Entered: boolean); virtual;
  24. procedure DocHEAD(Entered: boolean); virtual;
  25. procedure DocMETA; virtual;
  26. procedure DocTITLE(Entered: boolean); virtual;
  27. procedure DocBODY(Entered: boolean); virtual;
  28. procedure DocAnchor(Entered: boolean); virtual;
  29. procedure DocHeading(Level: integer; Entered: boolean); virtual;
  30. procedure DocParagraph(Entered: boolean); virtual;
  31. procedure DocBreak; virtual;
  32. procedure DocImage; virtual;
  33. procedure DocBold(Entered: boolean); virtual;
  34. procedure DocCite(Entered: boolean); virtual;
  35. procedure DocCode(Entered: boolean); virtual;
  36. procedure DocEmphasized(Entered: boolean); virtual;
  37. procedure DocItalic(Entered: boolean); virtual;
  38. procedure DocKbd(Entered: boolean); virtual;
  39. procedure DocPreformatted(Entered: boolean); virtual;
  40. procedure DocSample(Entered: boolean); virtual;
  41. procedure DocStrong(Entered: boolean); virtual;
  42. procedure DocTeleType(Entered: boolean); virtual;
  43. procedure DocVariable(Entered: boolean); virtual;
  44. procedure DocList(Entered: boolean); virtual;
  45. procedure DocOrderedList(Entered: boolean); virtual;
  46. procedure DocListItem; virtual;
  47. procedure DocDefList(Entered: boolean); virtual;
  48. procedure DocDefTerm; virtual;
  49. procedure DocDefExp; virtual;
  50. procedure DocHorizontalRuler; virtual;
  51. private
  52. Topic: PTopic;
  53. TopicLinks: PTopicLinkCollection;
  54. TextPtr: word;
  55. InTitle: boolean;
  56. InBody: boolean;
  57. InAnchor: boolean;
  58. InParagraph: boolean;
  59. InPreformatted: boolean;
  60. TopicTitle: string;
  61. Indent: integer;
  62. AnyCharsInLine: boolean;
  63. CurHeadLevel: integer;
  64. PAlign: TParagraphAlign;
  65. LinkIndexes: array[0..MaxTopicLinks] of sw_integer;
  66. LinkPtr: sw_integer;
  67. { Anchor: TAnchor;}
  68. procedure AddText(S: string);
  69. procedure AddChar(C: char);
  70. end;
  71. PHTMLHelpFile = ^THTMLHelpFile;
  72. THTMLHelpFile = object(THelpFile)
  73. constructor Init(AFileName: string; AID: word; ATOCEntry: string);
  74. destructor Done; virtual;
  75. public
  76. function LoadIndex: boolean; virtual;
  77. function SearchTopic(HelpCtx: THelpCtx): PTopic; virtual;
  78. function ReadTopic(T: PTopic): boolean; virtual;
  79. private
  80. Renderer: PHTMLTopicRenderer;
  81. FileName: string;
  82. CurFileName: string;
  83. TOCEntry: string;
  84. TopicLinks: PTopicLinkCollection;
  85. end;
  86. implementation
  87. uses Dos;
  88. const
  89. {$ifdef LINUX}
  90. dirsep = '/';
  91. {$else}
  92. dirsep = '\';
  93. {$endif}
  94. function FormatPath(Path: string): string;
  95. var P: sw_integer;
  96. begin
  97. repeat
  98. if DirSep='/' then P:=Pos('\',Path)
  99. else P:=Pos('/',Path);
  100. if P>0 then Path[P]:=DirSep;
  101. until P=0;
  102. FormatPath:=Path;
  103. end;
  104. function UpcaseStr(S: string): string;
  105. var I: integer;
  106. begin
  107. for I:=1 to length(S) do
  108. S[I]:=Upcase(S[I]);
  109. UpcaseStr:=S;
  110. end;
  111. function EncodeHTMLCtx(FileID: integer; LinkNo: word): longint;
  112. var Ctx: longint;
  113. begin
  114. Ctx:=(longint(FileID) shl 16)+LinkNo;
  115. EncodeHTMLCtx:=Ctx;
  116. end;
  117. procedure DecodeHTMLCtx(Ctx: longint; var FileID: word; var LinkNo: word);
  118. begin
  119. if (Ctx shr 16)=0 then
  120. begin
  121. FileID:=$ffff; LinkNo:=0;
  122. end
  123. else
  124. begin
  125. FileID:=Ctx shr 16; LinkNo:=Ctx and $ffff;
  126. end;
  127. end;
  128. function CharStr(C: char; Count: byte): string;
  129. var S: string;
  130. begin
  131. S[0]:=chr(Count);
  132. if Count>0 then FillChar(S[1],Count,C);
  133. CharStr:=S;
  134. end;
  135. procedure TTopicLinkCollection.Insert(Item: Pointer);
  136. begin
  137. AtInsert(Count,Item);
  138. end;
  139. function TTopicLinkCollection.At(Index: sw_Integer): PString;
  140. begin
  141. At:=inherited At(Index);
  142. end;
  143. function TTopicLinkCollection.AddItem(Item: string): integer;
  144. var Idx: sw_integer;
  145. begin
  146. if Item='' then Idx:=-1 else
  147. if Search(@Item,Idx)=false then
  148. begin
  149. AtInsert(Count,NewStr(Item));
  150. Idx:=Count-1;
  151. end;
  152. AddItem:=Idx;
  153. end;
  154. procedure THTMLTopicRenderer.DocAddTextChar(C: char);
  155. begin
  156. if InTitle then TopicTitle:=TopicTitle+C else
  157. if InBody then
  158. begin
  159. if (C<>#32) or (AnyCharsInLine=true) then AddChar(C);
  160. end;
  161. end;
  162. procedure THTMLTopicRenderer.DocSoftBreak;
  163. begin
  164. if InPreformatted then DocBreak else
  165. if AnyCharsInLine then AddChar(' ');
  166. end;
  167. procedure THTMLTopicRenderer.DocTYPE;
  168. begin
  169. end;
  170. procedure THTMLTopicRenderer.DocHTML(Entered: boolean);
  171. begin
  172. end;
  173. procedure THTMLTopicRenderer.DocHEAD(Entered: boolean);
  174. begin
  175. end;
  176. procedure THTMLTopicRenderer.DocMETA;
  177. begin
  178. end;
  179. procedure THTMLTopicRenderer.DocTITLE(Entered: boolean);
  180. begin
  181. if Entered then
  182. begin
  183. TopicTitle:='';
  184. end
  185. else
  186. begin
  187. { render topic title here }
  188. if TopicTitle<>'' then
  189. begin
  190. AddText(' '+TopicTitle+' Ü'); DocBreak;
  191. AddText(' '+CharStr('ß',length(TopicTitle)+3)); DocBreak;
  192. end;
  193. end;
  194. InTitle:=Entered;
  195. end;
  196. procedure THTMLTopicRenderer.DocBODY(Entered: boolean);
  197. begin
  198. InBody:=Entered;
  199. end;
  200. procedure THTMLTopicRenderer.DocAnchor(Entered: boolean);
  201. var HRef: string;
  202. begin
  203. if Entered and InAnchor then DocAnchor(false);
  204. if Entered then
  205. begin
  206. if DocGetTagParam('HREF',HRef)=false then HRef:='';
  207. if (HRef<>'') and (copy(HRef,1,1)<>'#') then
  208. begin
  209. InAnchor:=true;
  210. AddChar(hscLink);
  211. LinkIndexes[LinkPtr]:=TopicLinks^.AddItem(HRef);
  212. Inc(LinkPtr);
  213. end;
  214. end
  215. else
  216. begin
  217. if InAnchor=true then AddChar(hscLink);
  218. InAnchor:=false;
  219. end;
  220. end;
  221. procedure DecodeAlign(Align: string; var PAlign: TParagraphAlign);
  222. begin
  223. Align:=UpcaseStr(Align);
  224. if Align='LEFT' then PAlign:=paLeft else
  225. if Align='CENTER' then PAlign:=paCenter else
  226. if Align='RIGHT' then PAlign:=paRight;
  227. end;
  228. procedure THTMLTopicRenderer.DocHeading(Level: integer; Entered: boolean);
  229. var Align: string;
  230. begin
  231. if Entered then
  232. begin
  233. DocBreak;
  234. CurHeadLevel:=Level;
  235. PAlign:=paLeft;
  236. if DocGetTagParam('ALIGN',Align) then
  237. DecodeAlign(Align,PAlign);
  238. end
  239. else
  240. begin
  241. { if LastChar<>hscLineBreak then AddText(hscLineBreak);}
  242. CurHeadLevel:=0;
  243. DocBreak;
  244. end;
  245. end;
  246. procedure THTMLTopicRenderer.DocParagraph(Entered: boolean);
  247. var Align: string;
  248. begin
  249. { if Entered and InParagraph then}
  250. if Entered and InParagraph then DocParagraph(false);
  251. if Entered then
  252. begin
  253. if AnyCharsInLine then DocBreak;
  254. if DocGetTagParam('ALIGN',Align) then
  255. DecodeAlign(Align,PAlign);
  256. end
  257. else
  258. begin
  259. { if AnyCharsInLine then }DocBreak;
  260. PAlign:=paLeft;
  261. end;
  262. InParagraph:=Entered;
  263. end;
  264. procedure THTMLTopicRenderer.DocBreak;
  265. begin
  266. if (CurHeadLevel=1) or (PAlign=paCenter) then
  267. AddChar(hscCenter);
  268. if (PAlign=paRight) then
  269. AddChar(hscRight);
  270. AddChar(hscLineBreak);
  271. if Indent>0 then
  272. AddText(CharStr(#255,Indent)+hscLineStart);
  273. AnyCharsInLine:=false;
  274. end;
  275. procedure THTMLTopicRenderer.DocImage;
  276. var Alt: string;
  277. begin
  278. if DocGetTagParam('ALT',Alt)=false then Alt:='IMG';
  279. if Alt<>'' then
  280. begin
  281. AddText('['+Alt+']');
  282. end;
  283. end;
  284. procedure THTMLTopicRenderer.DocBold(Entered: boolean);
  285. begin
  286. end;
  287. procedure THTMLTopicRenderer.DocCite(Entered: boolean);
  288. begin
  289. end;
  290. procedure THTMLTopicRenderer.DocCode(Entered: boolean);
  291. begin
  292. end;
  293. procedure THTMLTopicRenderer.DocEmphasized(Entered: boolean);
  294. begin
  295. end;
  296. procedure THTMLTopicRenderer.DocItalic(Entered: boolean);
  297. begin
  298. end;
  299. procedure THTMLTopicRenderer.DocKbd(Entered: boolean);
  300. begin
  301. end;
  302. procedure THTMLTopicRenderer.DocPreformatted(Entered: boolean);
  303. begin
  304. if AnyCharsInLine then DocBreak;
  305. DocBreak;
  306. InPreformatted:=Entered;
  307. end;
  308. procedure THTMLTopicRenderer.DocSample(Entered: boolean);
  309. begin
  310. end;
  311. procedure THTMLTopicRenderer.DocStrong(Entered: boolean);
  312. begin
  313. end;
  314. procedure THTMLTopicRenderer.DocTeleType(Entered: boolean);
  315. begin
  316. end;
  317. procedure THTMLTopicRenderer.DocVariable(Entered: boolean);
  318. begin
  319. end;
  320. procedure THTMLTopicRenderer.DocList(Entered: boolean);
  321. begin
  322. if Entered then
  323. begin
  324. Inc(Indent,ListIndent);
  325. DocBreak;
  326. end
  327. else
  328. begin
  329. Dec(Indent,ListIndent);
  330. if AnyCharsInLine then DocBreak;
  331. end;
  332. end;
  333. procedure THTMLTopicRenderer.DocOrderedList(Entered: boolean);
  334. begin
  335. DocList(Entered);
  336. end;
  337. procedure THTMLTopicRenderer.DocListItem;
  338. begin
  339. if AnyCharsInLine then
  340. DocBreak;
  341. AddText('þ'+hscLineStart);
  342. end;
  343. procedure THTMLTopicRenderer.DocDefList(Entered: boolean);
  344. begin
  345. if Entered then
  346. begin
  347. { if LastChar<>hscLineBreak then DocBreak;}
  348. end
  349. else
  350. begin
  351. if AnyCharsInLine then DocBreak;
  352. end;
  353. end;
  354. procedure THTMLTopicRenderer.DocDefTerm;
  355. begin
  356. DocBreak;
  357. end;
  358. procedure THTMLTopicRenderer.DocDefExp;
  359. begin
  360. Inc(Indent,DefIndent);
  361. DocBreak;
  362. Dec(Indent,DefIndent);
  363. end;
  364. procedure THTMLTopicRenderer.DocHorizontalRuler;
  365. var OAlign: TParagraphAlign;
  366. begin
  367. OAlign:=PAlign;
  368. if AnyCharsInLine then DocBreak;
  369. PAlign:=paCenter;
  370. DocAddText(' '+CharStr('Ä',60)+' ');
  371. DocBreak;
  372. PAlign:=OAlign;
  373. end;
  374. procedure THTMLTopicRenderer.AddChar(C: char);
  375. begin
  376. if Topic=nil then Exit;
  377. Topic^.Text^[TextPtr]:=ord(C);
  378. Inc(TextPtr);
  379. if (C>#15) and (C<>' ') then
  380. AnyCharsInLine:=true;
  381. end;
  382. procedure THTMLTopicRenderer.AddText(S: string);
  383. var I: sw_integer;
  384. begin
  385. for I:=1 to length(S) do
  386. AddChar(S[I]);
  387. end;
  388. function THTMLTopicRenderer.BuildTopic(P: PTopic; HTMLFile: PTextFile; ATopicLinks: PTopicLinkCollection): boolean;
  389. var OK: boolean;
  390. TP: pointer;
  391. I: sw_integer;
  392. begin
  393. Topic:=P; TopicLinks:=ATopicLinks;
  394. OK:=Assigned(Topic) and Assigned(HTMLFile) and Assigned(TopicLinks);
  395. if OK then
  396. begin
  397. if (Topic^.TextSize<>0) and Assigned(Topic^.Text) then
  398. begin
  399. FreeMem(Topic^.Text,Topic^.TextSize);
  400. Topic^.TextSize:=0; Topic^.Text:=nil;
  401. end;
  402. Topic^.TextSize:=MaxHelpTopicSize;
  403. GetMem(Topic^.Text,Topic^.TextSize);
  404. TopicTitle:='';
  405. InTitle:=false; InBody:=false; InAnchor:=false;
  406. InParagraph:=false; InPreformatted:=false;
  407. Indent:=0; CurHeadLevel:=0;
  408. PAlign:=paLeft;
  409. TextPtr:=0; LinkPtr:=0;
  410. AnyCharsInLine:=false;
  411. OK:=Process(HTMLFile);
  412. if OK then
  413. begin
  414. { --- topic links --- }
  415. if (Topic^.Links<>nil) and (Topic^.LinkSize>0) then
  416. begin
  417. FreeMem(Topic^.Links,Topic^.LinkSize);
  418. Topic^.Links:=nil; Topic^.LinkSize:=0; Topic^.LinkCount:=0;
  419. end;
  420. Topic^.LinkCount:=TopicLinks^.Count;
  421. Topic^.LinkSize:=SizeOf(Topic^.Links^[0])*Topic^.LinkCount;
  422. GetMem(Topic^.Links,Topic^.LinkSize);
  423. for I:=0 to Topic^.LinkCount-1 do
  424. begin
  425. Topic^.Links^[I].FileID:=Topic^.FileID;
  426. Topic^.Links^[I].Context:=EncodeHTMLCtx(Topic^.FileID,LinkIndexes[I]+1);
  427. end;
  428. { --- topic text --- }
  429. GetMem(TP,TextPtr);
  430. Move(Topic^.Text^,TP^,TextPtr);
  431. FreeMem(Topic^.Text,Topic^.TextSize);
  432. Topic^.Text:=TP; Topic^.TextSize:=TextPtr;
  433. end
  434. else
  435. begin
  436. DisposeTopic(Topic);
  437. Topic:=nil;
  438. end;
  439. end;
  440. BuildTopic:=OK;
  441. end;
  442. constructor THTMLHelpFile.Init(AFileName: string; AID: word; ATOCEntry: string);
  443. begin
  444. inherited Init(AID);
  445. FileName:=AFileName; TOCEntry:=ATOCEntry;
  446. if FileName='' then Fail;
  447. New(Renderer, Init);
  448. New(TopicLinks, Init(50,500));
  449. end;
  450. function THTMLHelpFile.LoadIndex: boolean;
  451. begin
  452. IndexEntries^.Insert(NewIndexEntry(TOCEntry,ID,0));
  453. LoadIndex:=true;
  454. end;
  455. function THTMLHelpFile.SearchTopic(HelpCtx: THelpCtx): PTopic;
  456. function MatchCtx(P: PTopic): boolean; {$ifndef FPC}far;{$endif}
  457. begin
  458. MatchCtx:=P^.HelpCtx=HelpCtx;
  459. end;
  460. var FileID,LinkNo: word;
  461. P: PTopic;
  462. begin
  463. DecodeHTMLCtx(HelpCtx,FileID,LinkNo);
  464. if (HelpCtx<>0) and (FileID<>ID) then P:=nil else
  465. if (FileID=ID) and (LinkNo>TopicLinks^.Count) then P:=nil else
  466. begin
  467. P:=Topics^.FirstThat(@MatchCtx);
  468. if P=nil then
  469. begin
  470. P:=NewTopic(ID,HelpCtx,0);
  471. Topics^.Insert(P);
  472. end;
  473. end;
  474. SearchTopic:=P;
  475. end;
  476. function CompletePath(const Base: string; InComplete: string): string;
  477. var Drv,BDrv: string[40]; D,BD: DirStr; N,BN: NameStr; E,BE: ExtStr;
  478. P: sw_integer;
  479. begin
  480. FSplit(InComplete,D,N,E);
  481. P:=Pos(':',D); if P=0 then Drv:='' else begin Drv:=copy(D,1,P); Delete(D,1,P); end;
  482. FSplit(Base,BD,BN,BE);
  483. P:=Pos(':',BD); if P=0 then BDrv:='' else begin BDrv:=copy(BD,1,P); Delete(BD,1,P); end;
  484. if copy(D,1,1)<>'\' then
  485. InComplete:=BD+D+N+E;
  486. if Drv='' then
  487. InComplete:=BDrv+InComplete;
  488. InComplete:=FExpand(InComplete);
  489. CompletePath:=InComplete;
  490. end;
  491. function THTMLHelpFile.ReadTopic(T: PTopic): boolean;
  492. var OK: boolean;
  493. HTMLFile: PDOSTextFile;
  494. Name: string;
  495. Link: string;
  496. P: sw_integer;
  497. begin
  498. OK:=T<>nil;
  499. if OK then
  500. begin
  501. if T^.HelpCtx=0 then Name:=FileName else
  502. begin
  503. Link:=TopicLinks^.At(T^.HelpCtx-1)^;
  504. Link:=FormatPath(Link);
  505. P:=Pos('#',Link); if P>0 then Delete(Link,P,255);
  506. if CurFileName='' then Name:=Link else
  507. Name:=CompletePath(CurFileName,Link);
  508. end;
  509. New(HTMLFile, Init(Name));
  510. OK:=Renderer^.BuildTopic(T,HTMLFile,TopicLinks);
  511. if OK then CurFileName:=Name;
  512. if HTMLFile<>nil then Dispose(HTMLFile, Done);
  513. end;
  514. ReadTopic:=OK;
  515. end;
  516. destructor THTMLHelpFile.Done;
  517. begin
  518. inherited Done;
  519. if Renderer<>nil then Dispose(Renderer, Done);
  520. if TopicLinks<>nil then Dispose(TopicLinks, Done);
  521. end;
  522. END.