home *** CD-ROM | disk | FTP | other *** search
/ Piper's Pit BBS/FTP: ibm 0000 - 0009 / ibm0000-0009 / ibm0003.tar / ibm0003 / TPOWER52.ZIP / TPSRC1.ARC / TPHELP.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1989-07-10  |  59.3 KB  |  1,937 lines

  1. {$S-,R-,V-,I-,B-}
  2.  
  3. {$IFDEF Ver40}
  4.   {$F-}
  5.   {$DEFINE FMinus}
  6. {$ELSE}
  7.   {$F+}
  8.   {$I OPLUS.INC}
  9.   {$I AMINUS.INC}
  10. {$ENDIF}
  11.  
  12. {Conditional defines that may affect this unit}
  13. {$I TPDEFINE.INC}
  14.  
  15. {*********************************************************}
  16. {*                    TPHELP.PAS 5.07                    *}
  17. {*        Copyright (c) TurboPower Software 1987.        *}
  18. {* Portions copyright (c) Sunny Hill Software 1985, 1986 *}
  19. {*     and used under license to TurboPower Software     *}
  20. {*                 All rights reserved.                  *}
  21. {*********************************************************}
  22.  
  23. unit TpHelp;
  24.   {-General purpose help facility}
  25.  
  26. interface
  27.  
  28. uses
  29.   Dos,
  30.   TPDos,
  31.   TPMemChk,
  32.   TPString,
  33.   TpCrt,
  34.   TPWindow,
  35.   {$IFDEF UseMouse}
  36.   TpMouse,
  37.   {$ENDIF}
  38.   TpPick,
  39.   TpCmd;
  40.  
  41. const
  42.   NoHelpAvailable = $FFFFFFFF; {Flag that no help is available for topic}
  43.   MaxPagesPerSection = 20;   {Maximum number of pages of help per section}
  44.   MaxXrefsPerSection = 50;   {Maximum number of topic xrefs per section}
  45.   MaxTopics = 10000;         {Maximum number of topics in one help file}
  46.   MaxHelpStack = 19;         {Highest stacked topic}
  47.  
  48.   Attr1Toggle = ^A;          {Character toggles special attribute 1}
  49.   Attr2Toggle = ^B;          {Character toggles special attribute 2}
  50.   Attr3Toggle = ^C;          {Character toggles special attribute 3}
  51.   IndexMarker = ^D;          {Character marks topic number that follows}
  52.   XrefToggle = ^E;           {Character toggles xref highlight}
  53.   LineBrkMark = ^M;          {Denotes end of line of help}
  54.   PageBrkMark = ^L;          {Denotes end of page of help}
  55.   SectEndMark = #0;          {Denotes end of help section}
  56.  
  57.   HelpId : array[0..3] of Char = 'TPH2'; {Identifier at start of help file}
  58.  
  59.   {Command values for help system}
  60.   HKSNone = 0;               {Not a command}
  61.   HKSAlpha = 1;              {An alphanumeric character, ignored}
  62.   HKSUp = 2;                 {Cursor up to previous cross-reference}
  63.   HKSDown = 3;               {Cursor down to next cross-reference}
  64.   HKSPgUp = 4;               {Display previous help page}
  65.   HKSPgDn = 5;               {Display next help page}
  66.   HKSLeft = 6;               {Cursor left to previous cross-reference}
  67.   HKSRight = 7;              {Cursor right to next cross-reference}
  68.   HKSExit = 8;               {Exit the help system}
  69.   HKSSelect = 9;             {Select the current cross-reference and display topic}
  70.   HKSPrev = 10;              {Display the most recent help topic}
  71.   HKSHome = 11;              {Display first help page}
  72.   HKSEnd = 12;               {Display last help page}
  73.   HKSIndex = 13;             {Display an index of all help topics}
  74.   HKSProbe = 14;             {Mouse selection}
  75.   HKSUser0 = 15;             {User-defined exit commands}
  76.   HKSUser1 = 16;
  77.   HKSUser2 = 17;
  78.   HKSUser3 = 18;
  79.  
  80.   {.F-}
  81.   {Keystroke to command mapping}
  82.   HelpKeyMax = 99;
  83.   HelpKeyID : string[16] = 'tphelp key array';
  84.   HelpKeySet : array[0..HelpKeyMax] of Byte =
  85.   (
  86.   3, $00, $48, HKSUp,       {Up}
  87.   3, $00, $50, HKSDown,     {Down}
  88.   3, $00, $49, HKSPgUp,     {PgUp}
  89.   3, $00, $51, HKSPgDn,     {PgDn}
  90.   3, $00, $4B, HKSLeft,     {Left}
  91.   3, $00, $4D, HKSRight,    {Right}
  92.   3, $00, $68, HKSPrev,     {Alt-F1}
  93.   3, $00, $47, HKSHome,     {Home}
  94.   3, $00, $4F, HKSEnd,      {End}
  95.   3, $00, $3B, HKSIndex,    {F1}
  96.   2, $05,      HKSUp,       {^E}
  97.   2, $17,      HKSUp,       {^W}
  98.   2, $18,      HKSDown,     {^X}
  99.   2, $1A,      HKSDown,     {^Z}
  100.   2, $12,      HKSPgUp,     {^R}
  101.   2, $03,      HKSPgDn,     {^C}
  102.   2, $13,      HKSLeft,     {^S}
  103.   2, $04,      HKSRight,    {^D}
  104.   2, $1B,      HKSExit,     {Esc}
  105.   2, $0D,      HKSSelect,   {Enter}
  106.   3, $11, $12, HKSHome,     {^QR}
  107.   3, $11, $03, HKSEnd,      {^QC}
  108.   {$IFDEF UseMouse}
  109.   3, $00, $EF, HKSProbe,    {Click left}
  110.   3, $00, $EE, HKSExit,     {Click right}
  111.   3, $00, $ED, HKSIndex,    {Click both}
  112.   {$ELSE}
  113.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
  114.   0, 0,
  115.   {$ENDIF}
  116.   0, 0, 0, 0, 0, 0, 0, 0, 0, 0 {Space for customization}
  117.   );
  118.   {.F+}
  119.  
  120.   HelpTitle : string[39] = ' Topics '; {Displayed at top of help pick window}
  121.   UseHelpFrame : Boolean = True; {True to draw frame around help window}
  122.   HelpMore : Boolean = True; {True to display PgUp/PgDn in help frame}
  123.   HideCursor : Boolean = True; {False to leave hardware cursor on screen}
  124.  
  125.   IndexXrefTopic : Word = 0;  {Topic number which brings up index when used as xref}
  126.  
  127.   {$IFDEF UseMouse}
  128.   HelpMouseScroll : Boolean = True; {True to support mouse scrolling}
  129.   {$ENDIF}
  130.  
  131. type
  132.   HKtype = HKSNone..HKSUser3; {Valid help commands}
  133.  
  134.   HelpColorType = (
  135.     FrAttr,                  {Frame and more prompt}
  136.     TeAttr,                  {Normal text}
  137.     HeAttr,                  {Header}
  138.     XsAttr,                  {Selected cross-reference item}
  139.     XrAttr,                  {Unselected cross-reference item}
  140.     SpAtt1,                  {Special attribute #1}
  141.     SpAtt2,                  {Special attribute #2}
  142.     SpAtt3);                 {Special attribute #3}
  143.   HelpColorArray = array[HelpColorType] of Byte;
  144.   HelpToggleArray = array[Attr1Toggle..XrefToggle] of Byte;
  145.  
  146.  
  147.   XlateArray = array[0..15] of Byte; {Most common characters in help text}
  148.  
  149.   HelpHeader =               {At start of help file}
  150.     record
  151.       ID : LongInt;          {Marks file as help file}
  152.       HighestTopic : Word;   {Highest topic number}
  153.       BiggestTopic : Word;   {Size of largest topic in uncompressed bytes}
  154.       NamedTopics : Word;    {Number of topics in help index}
  155.       NameSize : Byte;       {Size of each entry in pick table, 0 for none}
  156.       Width : Byte;          {Width of help window, with frame if any}
  157.       XlateTable : XlateArray; {Table for decompression}
  158.     end;
  159.   HelpHeaderPtr = ^HelpHeader;
  160.  
  161.   CharArray = array[0..65520] of Char; {List of names of help entries}
  162.   CharArrayPtr = ^CharArray;
  163.  
  164.   HelpIndexRec =
  165.     record
  166.       Start : LongInt;       {File position of topic}
  167.       CompLen : Word;        {Compressed length of topic}
  168.     end;                     {Index of file positions}
  169.   HelpIndex = array[1..MaxTopics] of HelpIndexRec;
  170.   HelpIndexPtr = ^HelpIndex;
  171.  
  172.   TopicIndex = array[1..MaxTopics] of Word;
  173.   TopicIndexPtr = ^TopicIndex;
  174.  
  175.   Xrefs = 0..MaxXrefsPerSection;
  176.   Pages = 0..MaxPagesPerSection;
  177.  
  178.   HelpStackRec =
  179.     record
  180.       STopic : Word;         {Which topic}
  181.       SPage : Pages;         {Which page in that topic}
  182.       SXnum : Xrefs;         {Which xref item selected}
  183.     end;
  184.   HelpStackIndex = 0..MaxHelpStack;
  185.   HelpStackArray = array[HelpStackIndex] of HelpStackRec;
  186.  
  187.   HelpPtr = ^HelpDesc;       {The user hook to the help system}
  188.   HelpDesc =                 {Holds parameters of help system}
  189.     record
  190.       BufP : CharArrayPtr;   {Points to a buffer that will hold largest section}
  191.       Hdr : HelpHeader;      {Copy of header for fast reference}
  192.       RowH : Byte;           {Upper left corner of help window - Row}
  193.       ColH : Byte;           {Upper left corner of help window - Col}
  194.       Height : Byte;         {Height of help window, with frame}
  195.       PickCs : Byte;         {Columns of items to use in help index}
  196.       A : HelpColorArray;    {Attributes used to draw help}
  197.       Frame : FrameArray;    {Frame characters to use}
  198.       ShowFrame : Boolean;   {True to display frame}
  199.       ShowMore : Boolean;    {True to display More prompt in frame}
  200.       MouseScroll : Boolean; {True to display mouse scroll bar}
  201.       Stack : HelpStackArray; {Topics previously accessed}
  202.       St : HelpStackIndex;   {Top of help stack}
  203.       Sb : HelpStackIndex;   {Bottom of help stack}
  204.       case InRAM : Boolean of {True if help file is bound into code}
  205.         False :
  206.           (Fil : file);      {Untyped file variable for help}
  207.         True :
  208.           (HdrP : HelpHeaderPtr; {Points to base of structure in RAM}
  209.             NamP : CharArrayPtr; {Points to pick name array in RAM}
  210.             IndP : HelpIndexPtr); {Points to help section index in RAM}
  211.     end;
  212.  
  213. var
  214.   HelpKeyPtr : Pointer;      {Points to Kbd routine like ReadKeyWord}
  215.   HelpCmdNum : HKtype;       {Last help command entered}
  216.   HelpOnScreen : Boolean;    {True when help system is displayed}
  217.   {$IFDEF UseMouse}
  218.   HelpMouseEnabled : Boolean; {True if mouse is enabled}
  219.   {$ENDIF}
  220.  
  221.   {-----------------------------------------------------------------}
  222.  
  223. function OpenHelpFile(HelpFileName : string;
  224.                       XLow, YLow, YHigh, PickCols : Byte;
  225.                       Colors : HelpColorArray;
  226.                       var Help : HelpPtr) : Word;
  227.   {-Find and open help file, returning 0 or error code, and
  228.     an initialized help descriptor if successful}
  229.  
  230. function OpenHelpMem(HPtr : Pointer;
  231.                      XLow, YLow, YHigh, PickCols : Byte;
  232.                      Colors : HelpColorArray;
  233.                      var Help : HelpPtr) : Word;
  234.   {-Initialize help descriptor for a help structure bound into code}
  235.  
  236. procedure CloseHelp(var Help : HelpPtr);
  237.   {-Close help file and/or deallocate buffer space}
  238.  
  239. procedure SetHelpPos(Help : HelpPtr; XLow, YLow, YHigh : Byte);
  240.   {-Change the position and height of a help window}
  241.  
  242. function ShowHelp(Help : HelpPtr; Topic : Word) : Boolean;
  243.   {-Display help screen for item Topic, returning true if successful}
  244.  
  245. function FindHelp(Help : HelpPtr; Name : string; MatchFunc : Pointer) : Word;
  246.   {-Return Topic number of help with specified Name, 0 if not found}
  247.  
  248. function ScreenHelp(Help : HelpPtr; XPos, YPos : Byte;
  249.                     ScanBack : Boolean; MatchFunc : Pointer) : Word;
  250.   {-Return topic matching screen contents at position (XPos,YPos) or 0 if none}
  251.  
  252. function PickHelp(Help : HelpPtr; XLow, YLow, YHigh, PickCols : Byte) : Word;
  253.   {-Display help pick list, returning Topic number, or 0 for none}
  254.  
  255. function ShowPrevHelp(Help : HelpPtr) : Boolean;
  256.   {-Display help screen for topic most recently selected}
  257.  
  258. function AddHelpCommand(Cmd : HKtype; NumKeys : Byte; Key1, Key2 : Word) : Boolean;
  259.   {-Add a new command key assignment or change an existing one}
  260.  
  261. procedure DisableHelpIndex;
  262.   {-Disable the F1 help index inside of a help screen}
  263.  
  264. procedure EnableHelpIndex;
  265.   {-Enable the F1 help index inside of a help screen}
  266.  
  267.   {$IFDEF UseMouse}
  268. procedure EnableHelpMouse;
  269.   {-Enable mouse control of help screens}
  270.  
  271. procedure DisableHelpMouse;
  272.   {-Disable mouse control of help screens}
  273.   {$ENDIF}
  274.  
  275.   {=================================================================}
  276.  
  277. implementation
  278.  
  279. const
  280.   FlexStackSize = 6;         {Max number of nested attributes}
  281.   MouseUpMark = #24;         {Characters in scroll bar}
  282.   MouseDnMark = #25;
  283.   MousePrevMark = #4;
  284.   ScrollMark = #178;
  285.  
  286.   FrameDelta : array[Boolean] of Byte = (1, 0);
  287.  
  288.   HelpIndexDisabled : Boolean = False; {True when F1 inside of help is disabled}
  289.  
  290. type
  291.   SO =
  292.     record
  293.       O : Word;
  294.       S : Word;
  295.     end;
  296.  
  297.   StringPtr = ^string;
  298.  
  299.   PageIndex = array[Pages] of Word; {Offset into text for each page}
  300.  
  301.   FlexStack = array[0..FlexStackSize] of Byte; {Stacks current attributes}
  302.   PageAttrRec =
  303.     record
  304.       FlexSp : Byte;         {Current stack pointer}
  305.       FlexSt : FlexStack;    {Stack of active attributes}
  306.     end;
  307.   PageAttr = array[Pages] of PageAttrRec;
  308.  
  309.   XrefRec =
  310.     record
  311.       Page : Pages;          {Which page the xref displays on}
  312.       Row : Byte;            {Which row of window}
  313.       Col : Byte;            {Which col of window}
  314.       Len : Byte;            {Length of highlight}
  315.       Bofs : Word;           {Offset in uncompressed text buffer}
  316.       Topic : Word;          {Which topic is cross-referenced}
  317.     end;
  318.   XrefIndex = array[Xrefs] of XrefRec;
  319.  
  320.   HelpStateRec =
  321.     record
  322.       ColMin : Byte;         {Min window-relative col where text appears}
  323.       ColMax : Byte;         {Max window-relative col where text appears}
  324.       Pnum : Pages;          {Currently displayed help page}
  325.       Pcnt : Pages;          {Number of pages in topic}
  326.       P : PageIndex;         {Buffer offset of each page}
  327.       PA : PageAttr;         {Starting attribute state of each page}
  328.       Xnum : Xrefs;          {Currently selected cross-reference}
  329.       Xcnt : Xrefs;          {Number of cross-references in topic}
  330.       X : XrefIndex;         {Index of cross-references}
  331.       AC : HelpToggleArray;  {Attributes for each toggle character}
  332.       ShowMoreNow : Boolean; {True to display More prompt in frame}
  333.       MouseScrollNow : Boolean; {True to display mouse scroll bar}
  334.       Lnum : Pages;          {Last active page number}
  335.       Lslid : Byte;          {Last slider position}
  336.       W : WindowPtr;         {Pointer to window in which help appears}
  337.     end;
  338.  
  339. var
  340.   SPtr : CharArrayPtr;       {Source pointer}
  341.   Dptr : CharArrayPtr;       {Destination pointer}
  342.   SI : Word;                 {Source index during decompression}
  343.   DI : Word;                 {Destination index during decompression}
  344.   BN : Byte;                 {Buffered nibble}
  345.   Nibble : Boolean;          {True when partial byte entered during decompress}
  346.   PBuff : CharArrayPtr;      {Pointer to buffer of pick names}
  347.   TBuff : TopicIndexPtr;     {Pointer to buffer of topic index}
  348.   NSize : Byte;              {Size of array element in pick buffer}
  349.  
  350.   {$IFDEF FMinus}
  351.   {$F+}
  352.   {$ENDIF}
  353.   function SendHelpName(Topic : Word) : string;
  354.     {-Pass each help Topic to the pick unit}
  355.   begin
  356.     SendHelpName := ' '+string(Ptr(SO(PBuff).S, SO(PBuff).O+NSize*(TBuff^[Topic]-1))^);
  357.   end;
  358.  
  359.   function Match(S1, S2 : string) : Boolean;
  360.     {-Default match function}
  361.   begin
  362.     Match := (CompUCstring(S1, S2) = Equal);
  363.   end;
  364.   {$IFDEF FMinus}
  365.   {$F-}
  366.   {$ENDIF}
  367.  
  368.   function OpenHelpFile(HelpFileName : string;
  369.                         XLow, YLow, YHigh, PickCols : Byte;
  370.                         Colors : HelpColorArray;
  371.                         var Help : HelpPtr) : Word;
  372.     {-Find and open help file, returning 0 or error code, and
  373.       an initialized help descriptor if successful}
  374.   label
  375.     ErrorExit;
  376.   var
  377.     IO : Word;
  378.     BytesRead : Word;
  379.     IsOpen : Boolean;
  380.     MaxCols : Byte;
  381.   begin
  382.     {Initialize the result}
  383.     Help := nil;
  384.     IsOpen := False;
  385.  
  386.     {Find the help file}
  387.     if not ExistOnPath(HelpFileName, HelpFileName) then begin
  388.       OpenHelpFile := 2;
  389.       goto ErrorExit;
  390.     end;
  391.  
  392.     {Allocate space for help descriptor}
  393.     if not GetMemCheck(Help, SizeOf(HelpDesc)) then begin
  394.       OpenHelpFile := 203;
  395.       goto ErrorExit;
  396.     end;
  397.  
  398.     {Initialize the help descriptor}
  399.     with Help^ do begin
  400.       {Most help information is on disk}
  401.       InRAM := False;
  402.  
  403.       {Open the help file}
  404.       Assign(Fil, HelpFileName);
  405.       Reset(Fil, 1);
  406.       IO := IoResult;
  407.       if IO <> 0 then begin
  408.         OpenHelpFile := IO;
  409.         goto ErrorExit;
  410.       end;
  411.       IsOpen := True;
  412.  
  413.       {Get header from file}
  414.       BlockRead(Fil, Hdr, SizeOf(HelpHeader), BytesRead);
  415.       IO := IoResult;
  416.       if IO <> 0 then begin
  417.         OpenHelpFile := IO;
  418.         goto ErrorExit;
  419.       end;
  420.       if BytesRead <> SizeOf(HelpHeader) then begin
  421.         OpenHelpFile := 100;
  422.         goto ErrorExit;
  423.       end;
  424.  
  425.       with Hdr do begin
  426.         {Check file ID}
  427.         if ID <> LongInt(HelpId) then begin
  428.           {"Invalid numeric format" - used as error code for invalid ID}
  429.           OpenHelpFile := 106;
  430.           goto ErrorExit;
  431.         end;
  432.         {Get buffer space for reading help sections}
  433.         if not GetMemCheck(BufP, BiggestTopic) then begin
  434.           OpenHelpFile := 203;
  435.           goto ErrorExit;
  436.         end;
  437.  
  438.         {Validate number of pick columns}
  439.         if NameSize <= 1 then {!!.07}
  440.           MaxCols := 0        {!!.07}
  441.         else                  {!!.07}
  442.           MaxCols := (Width-4) div (NameSize-1);
  443.         if PickCols > MaxCols then
  444.           PickCs := MaxCols
  445.         else
  446.           PickCs := PickCols;
  447.       end;
  448.  
  449.       {Initialize remaining fields}
  450.       RowH := YLow;
  451.       ColH := XLow;
  452.       Height := YHigh-YLow+1;
  453.       A := Colors;
  454.       Frame := FrameChars;
  455.       ShowFrame := UseHelpFrame;
  456.       ShowMore := UseHelpFrame and HelpMore;
  457.       {$IFDEF UseMouse}
  458.       MouseScroll := UseHelpFrame and HelpMouseScroll;
  459.       {$ENDIF}
  460.       St := 0;
  461.       Sb := 0;
  462.  
  463.       {Successful initialization}
  464.       OpenHelpFile := 0;
  465.       Exit;
  466.     end;
  467.  
  468. ErrorExit:
  469.     if IsOpen then begin
  470.       Close(Help^.Fil);
  471.       IO := IoResult;
  472.     end;
  473.     FreeMemCheck(Help, SizeOf(HelpDesc));
  474.   end;
  475.  
  476.   function OpenHelpMem(HPtr : Pointer;
  477.                        XLow, YLow, YHigh, PickCols : Byte;
  478.                        Colors : HelpColorArray;
  479.                        var Help : HelpPtr) : Word;
  480.     {-Initialize help descriptor for a help structure bound into code}
  481.   label
  482.     ErrorExit;
  483.   var
  484.     MaxCols : Byte;
  485.   begin
  486.     {Initialize the result in case of failure}
  487.     Help := nil;
  488.  
  489.     {Allocate space for help descriptor}
  490.     if not GetMemCheck(Help, SizeOf(HelpDesc)) then begin
  491.       OpenHelpMem := 203;
  492.       goto ErrorExit;
  493.     end;
  494.  
  495.     {Initialize the help descriptor}
  496.     with Help^ do begin
  497.       {Help information is in RAM}
  498.       InRAM := True;
  499.  
  500.       {Check out header}
  501.       HdrP := HPtr;
  502.       Hdr := HdrP^;
  503.       with Hdr do begin
  504.         if ID <> LongInt(HelpId) then begin
  505.           {"Invalid numeric format" - used as error code for invalid ID}
  506.           OpenHelpMem := 106;
  507.           goto ErrorExit;
  508.         end;
  509.         {Get buffer space for decompressing help sections}
  510.         if not GetMemCheck(BufP, BiggestTopic) then begin
  511.           OpenHelpMem := 203;
  512.           goto ErrorExit;
  513.         end;
  514.         NamP := HPtr;
  515.         Inc(SO(NamP).O, SizeOf(HelpHeader));
  516.         IndP := HelpIndexPtr(NamP);
  517.         Inc(SO(IndP).O, HighestTopic*NameSize);
  518.  
  519.         {Validate number of pick columns}
  520.         if NameSize <= 1 then {!!.07}
  521.           MaxCols := 0
  522.         else
  523.           MaxCols := (Width-4) div (NameSize-1);
  524.         if PickCols > MaxCols then
  525.           PickCs := MaxCols
  526.         else
  527.           PickCs := PickCols;
  528.       end;
  529.  
  530.       {Initialize remaining fields}
  531.       RowH := YLow;
  532.       ColH := XLow;
  533.       Height := YHigh-YLow+1;
  534.       A := Colors;
  535.       Frame := FrameChars;
  536.       ShowFrame := UseHelpFrame;
  537.       ShowMore := UseHelpFrame and HelpMore;
  538.       {$IFDEF UseMouse}
  539.       MouseScroll := UseHelpFrame and HelpMouseScroll;
  540.       {$ENDIF}
  541.       St := 0;
  542.       Sb := 0;
  543.  
  544.       {Successful initialization}
  545.       OpenHelpMem := 0;
  546.       Exit;
  547.     end;
  548.  
  549. ErrorExit:
  550.     FreeMemCheck(Help, SizeOf(HelpDesc));
  551.   end;
  552.  
  553.   procedure CloseHelp(var Help : HelpPtr);
  554.     {-Close help file and/or deallocate buffer space}
  555.   var
  556.     IO : Word;
  557.   begin
  558.     with Help^, Hdr do begin
  559.       if ID <> LongInt(HelpId) then
  560.         {Not a valid help pointer}
  561.         Exit;
  562.       if not InRAM then
  563.         if FileRec(Fil).Mode = fmInOut then begin
  564.           {Close help file}
  565.           Close(Fil);
  566.           IO := IoResult;
  567.         end;
  568.       FreeMem(BufP, BiggestTopic);
  569.     end;
  570.     FreeMem(Help, SizeOf(HelpDesc));
  571.     Help := nil;
  572.   end;
  573.  
  574.   procedure SetHelpPos(Help : HelpPtr; XLow, YLow, YHigh : Byte);
  575.     {-Change the position of a help window}
  576.   begin
  577.     with Help^ do
  578.       if Hdr.ID = LongInt(HelpId) then begin
  579.         RowH := YLow;
  580.         ColH := XLow;
  581.         Height := YHigh-YLow+1;
  582.       end;
  583.   end;
  584.  
  585.   function GetNameString(Help : HelpPtr; Topic : Word) : string;
  586.     {-Return name string for help item, if any}
  587.   var
  588.     S : string;
  589.   begin
  590.     GetNameString := '';
  591.     with Help^, Hdr do
  592.       if NameSize <> 0 then
  593.         if InRAM then
  594.           GetNameString := string(Ptr(SO(NamP).S, SO(NamP).O+NameSize*(Topic-1))^)
  595.         else if FileRec(Fil).Mode = fmInOut then begin
  596.           Seek(Fil, LongInt(SizeOf(HelpHeader))+NameSize*(Topic-1));
  597.           if IoResult <> 0 then
  598.             Exit;
  599.           BlockRead(Fil, S, NameSize);
  600.           if IoResult <> 0 then
  601.             Exit;
  602.           GetNameString := S;
  603.         end;
  604.   end;
  605.  
  606.   procedure InitTopic(Help : HelpPtr; var HelpState : HelpStateRec);
  607.     {-Paginate topic and find xref points}
  608.   var
  609.     Bpos : Word;
  610.     Pofs : Word;
  611.     Prow : Word;
  612.     Pcol : Word;
  613.     Mrow : Word;
  614.     WordP : ^Word;
  615.     Done : Boolean;
  616.     Ch : Char;
  617.     HA : PageAttrRec;
  618.     LA : PageAttrRec;
  619.  
  620.     procedure NewPage;
  621.       {-Store information about previous page}
  622.     begin
  623.       with HelpState do
  624.         if Pcnt+1 >= MaxPagesPerSection then
  625.           Done := True
  626.         else begin
  627.           Inc(Pcnt);         {Increment page count}
  628.           P[Pcnt] := Pofs;   {Character offset at start of page}
  629.           PA[Pcnt] := LA;    {Attribute state at start of page}
  630.           Pofs := Bpos+1;    {Start of next page}
  631.           P[Pcnt+1] := Pofs; {Sentinel to end last page}
  632.           Prow := 0;         {New page has no lines}
  633.           LA := HA;          {Attribute state at start of new page}
  634.         end;
  635.     end;
  636.  
  637.   begin
  638.     with Help^, Hdr, HelpState do begin
  639.       Lnum := 0;
  640.       Pnum := 1;
  641.       Xnum := 0;
  642.       ColMin := 2;
  643.       ColMax := Width-3;
  644.       Bpos := 0;
  645.       Pcnt := 0;
  646.       Pofs := 0;
  647.       Prow := 0;
  648.       Pcol := ColMin;
  649.       Mrow := Height-2;
  650.       Xcnt := 0;
  651.  
  652.       {No special attributes initially active}
  653.       FillChar(HA, SizeOf(PageAttrRec), 0);
  654.       HA.FlexSt[0] := A[TeAttr];
  655.       LA := HA;
  656.  
  657.       Done := False;
  658.       repeat
  659.         Ch := BufP^[Bpos];
  660.         case Ch of
  661.           Attr1Toggle..Attr3Toggle : {Modifying video attribute}
  662.             with HA do
  663.               if (FlexSp > 0) and (FlexSt[FlexSp] = AC[Ch]) then
  664.                 {Toggling current state off}
  665.                 Dec(FlexSp)
  666.               else if FlexSp < FlexStackSize then begin
  667.                 {Changing to new attribute}
  668.                 Inc(FlexSp);
  669.                 FlexSt[FlexSp] := AC[Ch];
  670.               end;
  671.  
  672.           XrefToggle :       {Marking a cross-reference}
  673.             with HA do
  674.               if (FlexSp > 0) and (FlexSt[FlexSp] = A[XrAttr]) then begin
  675.                 {Toggling current state off}
  676.                 Dec(FlexSp);
  677.                 {Store length of highlight}
  678.                 with X[Xcnt] do
  679.                   Len := Bpos-Bofs-1;
  680.               end else if FlexSp < FlexStackSize then begin
  681.                 {Changing to new attribute}
  682.                 Inc(FlexSp);
  683.                 FlexSt[FlexSp] := A[XrAttr];
  684.               end;
  685.  
  686.           IndexMarker :      {Indicating cross-reference topic}
  687.             begin
  688.               if Xcnt < MaxXrefsPerSection then begin
  689.                 Inc(Xcnt);
  690.                 with X[Xcnt] do begin
  691.                   Page := Pcnt+1;
  692.                   Row := Prow+1;
  693.                   Col := Pcol;
  694.                   Bofs := Bpos+3;
  695.                   WordP := @BufP^[Bpos+1];
  696.                   Topic := WordP^;
  697.                 end;
  698.               end;
  699.               Inc(Bpos, 2);
  700.             end;
  701.  
  702.           LineBrkMark :      {End of line}
  703.             begin
  704.               Inc(Prow);
  705.               Pcol := ColMin;
  706.               if Prow >= Mrow then
  707.                 NewPage;
  708.             end;
  709.  
  710.           PageBrkMark :      {End of page}
  711.             if Bpos = Pofs then
  712.               Inc(Pofs)
  713.             else begin
  714.               Pcol := ColMin;
  715.               NewPage;
  716.             end;
  717.  
  718.           SectEndMark :      {End of section}
  719.             begin
  720.               if Bpos <> Pofs then
  721.                 NewPage;
  722.               Done := True;
  723.             end;
  724.         else
  725.           Inc(Pcol);
  726.         end;
  727.         Inc(Bpos);
  728.       until Done;
  729.     end;
  730.   end;
  731.  
  732.   procedure ShowPrompt(Help : HelpPtr; var HelpState : HelpStateRec);
  733.     {-Show information about help}
  734.   const
  735.     MoreLen = 11;
  736.     Up : string[4] = 'PgUp';
  737.     Dn : string[4] = 'PgDn';
  738.   var
  739.     MoreMsg : string[11];
  740.   begin
  741.     with Help^, Hdr, HelpState do begin
  742.       if (Width < MoreLen+6) then
  743.         Exit;
  744.       FillChar(MoreMsg[1], MoreLen, FrameChars[Horiz]);
  745.       MoreMsg[0] := Char(MoreLen);
  746.       MoreMsg[6] := ' ';
  747.       if Pnum > 1 then begin
  748.         Move(Up[1], MoreMsg[2], 4);
  749.         MoreMsg[1] := ' ';
  750.       end;
  751.       if Pnum < Pcnt then begin
  752.         Move(Dn[1], MoreMsg[7], 4);
  753.         MoreMsg[11] := ' ';
  754.         if Pnum > 1 then
  755.           MoreMsg[6] := '/'
  756.       end;
  757.       FastWrite(MoreMsg, RowH+Height-1, ColH+Width-MoreLen-3, A[FrAttr]);
  758.     end;
  759.   end;
  760.  
  761.   procedure DrawPage(Help : HelpPtr; var HelpState : HelpStateRec);
  762.     {-Draw one page of help}
  763.   var
  764.     Bpos : Word;
  765.     Bend : Word;
  766.     Attr : Byte;
  767.     R : Byte;
  768.     C : Byte;
  769.     Ch : Char;
  770.     AtSt : PageAttrRec;
  771.   begin
  772.     with Help^, HelpState, AtSt do begin
  773.       Bpos := P[Pnum];
  774.       Bend := P[Pnum+1];
  775.       R := 1;
  776.       C := ColMin;
  777.  
  778.       AtSt := PA[Pnum];
  779.       Attr := FlexSt[FlexSp];
  780.       ClrScr;
  781.  
  782.       repeat
  783.         Ch := BufP^[Bpos];
  784.         case Ch of
  785.           LineBrkMark :
  786.             begin
  787.               Inc(R);
  788.               C := ColMin;
  789.             end;
  790.  
  791.           Attr1Toggle..Attr3Toggle :
  792.             if (FlexSp > 0) and (FlexSt[FlexSp] = AC[Ch]) then begin
  793.               {Toggling current state off}
  794.               Dec(FlexSp);
  795.               Attr := FlexSt[FlexSp];
  796.             end else if FlexSp < FlexStackSize then begin
  797.               {Changing to new attribute}
  798.               Inc(FlexSp);
  799.               Attr := AC[Ch];
  800.               FlexSt[FlexSp] := Attr;
  801.             end;
  802.  
  803.           XrefToggle :
  804.             if (FlexSp > 0) and (FlexSt[FlexSp] = A[XrAttr]) then begin
  805.               {Toggling current state off}
  806.               Dec(FlexSp);
  807.               Attr := FlexSt[FlexSp];
  808.             end else if FlexSp < FlexStackSize then begin
  809.               {Changing to new attribute}
  810.               Inc(FlexSp);
  811.               if Bpos = X[Xnum].Bofs then
  812.                 {Selected cross-ref}
  813.                 Attr := A[XsAttr]
  814.               else
  815.                 {Deselected cross-ref}
  816.                 Attr := A[XrAttr];
  817.               FlexSt[FlexSp] := A[XrAttr];
  818.             end;
  819.  
  820.           IndexMarker :
  821.             {Skip over topic number}
  822.             Inc(Bpos, 2);
  823.  
  824.           PageBrkMark, SectEndMark :
  825.             Exit;
  826.         else
  827.           if C <= ColMax then
  828.             FastWriteWindow(Ch, R, C, Attr);
  829.           Inc(C);
  830.         end;
  831.         Inc(Bpos);
  832.       until Bpos >= Bend;
  833.     end;
  834.   end;
  835.  
  836.   procedure DrawXref(Help : HelpPtr; var HelpState : HelpStateRec; Num : Xrefs);
  837.     {-Draw Xref in appropriate attribute}
  838.   var
  839.     Bpos : Word;
  840.     Bend : Word;
  841.     Attr : Byte;
  842.     R : Byte;
  843.     C : Byte;
  844.     Ch : Char;
  845.   begin
  846.     with Help^, HelpState, X[Num] do begin
  847.       Bpos := Bofs+1;
  848.       Bend := P[Pnum+1];
  849.       R := Row;
  850.       C := Col;
  851.       if Num = Xnum then
  852.         Attr := A[XsAttr]
  853.       else
  854.         Attr := A[XrAttr];
  855.  
  856.       repeat
  857.         Ch := BufP^[Bpos];
  858.         case Ch of
  859.           LineBrkMark :
  860.             begin
  861.               Inc(R);
  862.               C := ColMin;
  863.             end;
  864.           XrefToggle :
  865.             Exit;
  866.           PageBrkMark, SectEndMark :
  867.             Exit;
  868.         else
  869.           if C <= ColMax then
  870.             FastWriteWindow(Ch, R, C, Attr);
  871.           Inc(C);
  872.         end;
  873.         Inc(Bpos);
  874.       until Bpos >= Bend;
  875.     end;
  876.   end;
  877.  
  878.   function GetNibble : Byte;
  879.     {-Return next nibble from source}
  880.   begin
  881.     if Nibble then begin
  882.       {Buffered nibble available}
  883.       GetNibble := BN shr 4;
  884.       Nibble := False;
  885.       Inc(SI);
  886.     end else begin
  887.       {First nibble of byte}
  888.       BN := Ord(SPtr^[SI]);
  889.       GetNibble := BN and $0F;
  890.       Nibble := True;
  891.     end;
  892.   end;
  893.  
  894.   procedure Decompress(var X : XlateArray; Len : Word; S, D : CharArrayPtr);
  895.     {-Decompress text of length Len at S to position D, using X for translation}
  896.   var
  897.     N : Byte;
  898.     C : Char;
  899.   begin
  900.     Nibble := False;
  901.     SI := 0;
  902.     DI := 0;
  903.     SPtr := S;
  904.     Dptr := D;
  905.     while SI < Len do begin
  906.       N := GetNibble;
  907.       if N < $0F then
  908.         C := Char(X[N])
  909.       else begin
  910.         N := GetNibble;
  911.         C := Char((GetNibble shl 4) or N);
  912.       end;
  913.       Dptr^[DI] := C;
  914.       Inc(DI);
  915.     end;
  916.   end;
  917.  
  918.   function LoadHelp(Help : HelpPtr; Topic : Word) : Boolean;
  919.     {-Load and decompress one help topic}
  920.   var
  921.     BytesRead : Word;
  922.     Frec : HelpIndexRec;
  923.     Comp : CharArrayPtr;
  924.   begin
  925.     LoadHelp := False;
  926.  
  927.     with Help^, Hdr do begin
  928.       if InRAM then begin
  929.         {Already in memory, just compute the pointer}
  930.         Frec := IndP^[Topic];
  931.         {Check for available help}
  932.         if Frec.Start = NoHelpAvailable then
  933.           Exit;
  934.         Comp := Ptr(SO(HdrP).S, SO(HdrP).O+Frec.Start);
  935.  
  936.       end else if FileRec(Fil).Mode = fmInOut then begin
  937.         {On disk, first read the index}
  938.         Seek(Fil, (SizeOf(HelpHeader)+
  939.           LongInt(NameSize)*HighestTopic+
  940.           SizeOf(HelpIndexRec)*(Topic-1)));
  941.         if IoResult <> 0 then
  942.           Exit;
  943.         BlockRead(Fil, Frec, SizeOf(HelpIndexRec), BytesRead);
  944.         if (IoResult <> 0) or (BytesRead <> SizeOf(HelpIndexRec)) then
  945.           Exit;
  946.         {Check for available help}
  947.         if Frec.Start = NoHelpAvailable then
  948.           Exit;
  949.         {Now read the help section}
  950.         Seek(Fil, Frec.Start);
  951.         if IoResult <> 0 then
  952.           Exit;
  953.         {Put compressed version at top of buffer}
  954.         Comp := @BufP^[BiggestTopic-Frec.CompLen];
  955.         BlockRead(Fil, Comp^, Frec.CompLen, BytesRead);
  956.         if (IoResult <> 0) or (BytesRead <> Frec.CompLen) then
  957.           Exit;
  958.       end else
  959.         {Help file not open}
  960.         Exit;
  961.  
  962.       {Decompress text into BufP^[0]}
  963.       Decompress(XlateTable, Frec.CompLen, Comp, BufP);
  964.  
  965.       LoadHelp := True;
  966.     end;
  967.   end;
  968.  
  969.   function FirstXref(var HelpState : HelpStateRec) : Xrefs;
  970.     {-Return index of first xref on page, 0 if none}
  971.   var
  972.     Inum : Xrefs;
  973.   begin
  974.     with HelpState do
  975.       for Inum := 1 to Xcnt do
  976.         if X[Inum].Page = Pnum then begin
  977.           FirstXref := Inum;
  978.           Exit;
  979.         end;
  980.     FirstXref := 0;
  981.   end;
  982.  
  983.   {$IFDEF UseMouse}
  984.   function MatchXref(Help : HelpPtr; var HelpState : HelpStateRec;
  985.                      Num : Xrefs; MX, MY : Byte) : Boolean;
  986.     {-Return true if any portion of xref intersects MX, MY}
  987.   var
  988.     Bpos : Word;
  989.     Bend : Word;
  990.     R : Byte;
  991.     C : Byte;
  992.   begin
  993.     MatchXref := False;
  994.     with Help^, HelpState, X[Num] do begin
  995.       Bpos := Bofs+1;
  996.       Bend := P[Pnum+1];
  997.       R := Row;
  998.       C := Col;
  999.       repeat
  1000.         case BufP^[Bpos] of
  1001.           LineBrkMark :
  1002.             begin
  1003.               Inc(R);
  1004.               C := ColMin;
  1005.             end;
  1006.           XrefToggle :
  1007.             Exit;
  1008.           PageBrkMark, SectEndMark :
  1009.             Exit;
  1010.         else
  1011.           {Check for a match}
  1012.           if (R = MY) and (C = MX) then begin
  1013.             MatchXref := True;
  1014.             Exit;
  1015.           end;
  1016.           Inc(C);
  1017.         end;
  1018.         Inc(Bpos);
  1019.       until Bpos >= Bend;
  1020.     end;
  1021.   end;
  1022.  
  1023.   function IsXRef(Help : HelpPtr; var HelpState : HelpStateRec;
  1024.                   MX, MY : Byte) : Xrefs;
  1025.     {-Select xref, if any, at position MX,MY}
  1026.   var
  1027.     Inum : Xrefs;
  1028.   begin
  1029.     with HelpState do
  1030.       for Inum := 1 to Xcnt do
  1031.         if X[Inum].Page = Pnum then
  1032.           if MatchXref(Help, HelpState, Inum, MX, MY) then begin
  1033.             IsXRef := Inum;
  1034.             Exit;
  1035.           end;
  1036.     IsXRef := 0;
  1037.   end;
  1038.  
  1039.   function SliderPos(var HelpState : HelpStateRec) : Byte;
  1040.     {-Calculate the slider position in absolute coordinates}
  1041.   begin
  1042.     with HelpState, WindowP(W)^ do
  1043.       SliderPos := YL+((Pnum-1)*(YH-YL)) div (Pcnt-1);
  1044.   end;
  1045.  
  1046.   procedure UpdateMouseFrame(Help : HelpPtr;
  1047.                              var HelpState : HelpStateRec);
  1048.     {-Set mouse window coordinates and scroll bar}
  1049.   begin
  1050.     with Help^, HelpState, WindowP(W)^, Draw do begin
  1051.       MouseScrollNow := MouseScroll and (Pcnt > 1);
  1052.       if MouseScrollNow then begin
  1053.         {Let mouse move into frame}
  1054.         MouseWindow(XL1, YL1, XH1, YH1);
  1055.         {Draw scroll marks}
  1056.         FastWrite(MouseUpMark, YL1, XH1, FAttr);
  1057.         FastWrite(MouseDnMark, YH1, XH1, FAttr);
  1058.         ShowMoreNow := False;
  1059.         Lslid := 0;
  1060.       end else
  1061.         {Don't let mouse move into frame}
  1062.         MouseWindow(XL1, YL1, XH, YH);
  1063.       {Draw previous help mark}
  1064.       FastWrite(MousePrevMark, YL1, XL1, FAttr);
  1065.     end;
  1066.   end;
  1067.   {$ENDIF}
  1068.  
  1069.   procedure IncPrim(var HelpState : HelpStateRec; Delta : Integer);
  1070.     {-Increment or decrement to next valid Xref}
  1071.   var
  1072.     Inum : Xrefs;
  1073.   begin
  1074.     with HelpState do
  1075.       if Xnum <> 0 then begin
  1076.         Inum := Xnum;
  1077.         repeat
  1078.           Inc(Xnum, Delta);
  1079.           if Xnum < 1 then
  1080.             Xnum := Xcnt
  1081.           else if Xnum > Xcnt then
  1082.             Xnum := 1;
  1083.         until (Xnum = Inum) or (X[Xnum].Page = Pnum);
  1084.       end;
  1085.   end;
  1086.  
  1087.   procedure IncXref(Help : HelpPtr; var HelpState : HelpStateRec;
  1088.                     Delta : Integer);
  1089.     {-Increment or decrement to next valid Xref and update screen}
  1090.   var
  1091.     Inum : Xrefs;
  1092.   begin
  1093.     with HelpState do begin
  1094.       Inum := Xnum;
  1095.       IncPrim(HelpState, Delta);
  1096.       if Inum <> Xnum then begin
  1097.         {Update highlights}
  1098.         DrawXref(Help, HelpState, Inum);
  1099.         DrawXref(Help, HelpState, Xnum);
  1100.       end;
  1101.     end;
  1102.   end;
  1103.  
  1104.   procedure IncVertXref(Help : HelpPtr; var HelpState : HelpStateRec;
  1105.                         Delta : Integer);
  1106.     {-Increment or decrement to next valid Xref}
  1107.   var
  1108.     Inum : Xrefs;
  1109.     Jnum : Xrefs;
  1110.   begin
  1111.     with HelpState do begin
  1112.       {Move to a different row}
  1113.       Inum := Xnum;
  1114.       repeat
  1115.         IncPrim(HelpState, Delta);
  1116.       until (Xnum = Inum) or (X[Xnum].Row <> X[Inum].Row);
  1117.  
  1118.       {Move to appropriate field on that row}
  1119.       if Delta*X[Xnum].Col < Delta*X[Inum].Col then
  1120.         repeat
  1121.           {Store previous xref}
  1122.           Jnum := Xnum;
  1123.           IncPrim(HelpState, Delta);
  1124.           if (Xnum <> Jnum) then
  1125.             {Able to move}
  1126.             if X[Xnum].Row <> X[Jnum].Row then
  1127.               {Moved to new row, back up}
  1128.               Xnum := Jnum
  1129.             else if Delta*X[Xnum].Col >= Delta*X[Inum].Col then {!!.07}
  1130.               {Far enough, force exit}
  1131.               Jnum := Xnum;
  1132.         until (Xnum = Jnum);
  1133.  
  1134.       if Inum <> Xnum then begin
  1135.         {Update highlights}
  1136.         DrawXref(Help, HelpState, Inum);
  1137.         DrawXref(Help, HelpState, Xnum);
  1138.       end;
  1139.     end;
  1140.   end;
  1141.  
  1142.   procedure GetHeaderString(Help : HelpPtr; Topic : Word;
  1143.                             var HeaderStr : string);
  1144.     {-Return string for header of window}
  1145.   begin
  1146.     HeaderStr := GetNameString(Help, Topic);
  1147.     if Length(HeaderStr) > 0 then
  1148.       HeaderStr := ' '+HeaderStr+' ';
  1149.   end;
  1150.  
  1151.   procedure FrameHelp(Help : HelpPtr; var HelpState : HelpStateRec;
  1152.                       Title : string);
  1153.     {-Draw titled frame around help window}
  1154.   begin
  1155.     with Help^, Hdr, HelpState do
  1156.       if ShowFrame then
  1157.         FrameWindow(ColH, RowH, ColH+Width-1, RowH+Height-1,
  1158.           A[FrAttr], A[HeAttr], Title);
  1159.   end;
  1160.  
  1161.   function LoadNewTopic(Help : HelpPtr; var HelpState : HelpStateRec;
  1162.                         Topic : Word) : Boolean;
  1163.     {-Return true if specified topic successfully loaded}
  1164.   var
  1165.     HeaderStr : string[80];
  1166.   begin
  1167.     with Help^, Hdr, HelpState do
  1168.       if LoadHelp(Help, Topic) then begin
  1169.         InitTopic(Help, HelpState);
  1170.         GetHeaderString(Help, Topic, HeaderStr);
  1171.         FrameHelp(Help, HelpState, HeaderStr);
  1172.         LoadNewTopic := True;
  1173.       end else
  1174.         LoadNewTopic := False;
  1175.   end;
  1176.  
  1177.   procedure IncSp(var Sp : HelpStackIndex);
  1178.     {-Increment and wrap}
  1179.   begin
  1180.     if Sp = MaxHelpStack then
  1181.       Sp := 0
  1182.     else
  1183.       Inc(Sp);
  1184.   end;
  1185.  
  1186.   procedure DecSp(var Sp : HelpStackIndex);
  1187.     {-Decrement and wrap}
  1188.   begin
  1189.     if Sp = 0 then
  1190.       Sp := MaxHelpStack
  1191.     else
  1192.       Dec(Sp);
  1193.   end;
  1194.  
  1195.   procedure PushStack(Help : HelpPtr;
  1196.                       Topic : Word;
  1197.                       Page : Pages;
  1198.                       Xnum : Xrefs);
  1199.     {-Push a help topic onto stack}
  1200.   begin
  1201.     with Help^ do
  1202.       if Topic <> 0 then begin {!!.06}
  1203.         with Stack[St] do begin
  1204.           STopic := Topic;
  1205.           SPage := Page;
  1206.           SXnum := Xnum;
  1207.         end;
  1208.         IncSp(St);
  1209.         if St = Sb then
  1210.           IncSp(Sb);
  1211.       end;
  1212.   end;
  1213.  
  1214.   procedure PopStack(Help : HelpPtr;
  1215.                      var Topic : Word;
  1216.                      var Page : Pages;
  1217.                      var Xnum : Xrefs);
  1218.     {-Pop help topic from stack}
  1219.   begin
  1220.     with Help^ do
  1221.       if St = Sb then {!!.07}
  1222.         Topic := 0    {!!.07}
  1223.       else begin      {!!.07}
  1224.         DecSp(St);
  1225.         with Stack[St] do begin
  1226.           Topic := STopic;
  1227.           Page := SPage;
  1228.           Xnum := SXnum;
  1229.         end;
  1230.       end;            {!!.07}
  1231.   end;
  1232.  
  1233.   function GetBuffer(Help : HelpPtr;
  1234.                      var P;
  1235.                      SeekOfs : LongInt;
  1236.                      SizeReq : Word;
  1237.                      var SizeAlloc : Word) : Boolean;
  1238.     {-Return pointer to loaded array of help data}
  1239.   var
  1240.     Pt : Pointer absolute P;
  1241.     BytesRead : Word;
  1242.   begin
  1243.     GetBuffer := False;
  1244.     SizeAlloc := 0;
  1245.  
  1246.     with Help^, Hdr do
  1247.       if InRAM then
  1248.         {Already in memory, just compute the pointer}
  1249.         Pt := Ptr(SO(HdrP).S, SO(HdrP).O+Word(SeekOfs))
  1250.  
  1251.       else if FileRec(Fil).Mode = fmInOut then begin
  1252.         {On disk, first allocate space}
  1253.         if not GetMemCheck(P, SizeReq) then
  1254.           Exit;
  1255.         SizeAlloc := SizeReq;
  1256.         {Read names into buffer}
  1257.         Seek(Fil, SeekOfs);
  1258.         if IoResult <> 0 then
  1259.           Exit;
  1260.         BlockRead(Fil, Pt^, SizeReq, BytesRead);
  1261.         if (IoResult <> 0) or (BytesRead <> SizeReq) then
  1262.           Exit;
  1263.  
  1264.       end else
  1265.         {Help file not open}
  1266.         Exit;
  1267.  
  1268.     GetBuffer := True;
  1269.   end;
  1270.  
  1271.   function ShowHelpPrim(Help : HelpPtr;
  1272.                         Topic : Word;
  1273.                         Page : Pages;
  1274.                         XrefNum : Xrefs) : Boolean;
  1275.     {-Display help screen, returning true if successful}
  1276.   label
  1277.     SelectATopic,
  1278.     LoadPrevTopic,
  1279.     ShowIndex,
  1280.     ExitPoint;
  1281.   var
  1282.     Done : Boolean;
  1283.     HaveBuffers : Boolean;
  1284.     Choice : Word;
  1285.     Row : Word;
  1286.     ChWord : Word;
  1287.     SizeAlloc1 : Word;
  1288.     SizeAlloc2 : Word;
  1289.     SavePickMatrix : Word;
  1290.     Key1 : Word;
  1291.     Key2 : Word;
  1292.     SavePickHelp : Pointer;  {!! 5.07}
  1293.     NumKeys : Byte;
  1294.  
  1295.     {$IFDEF UseMouse}
  1296.     MX : Byte;               {Mouse absolute X position}
  1297.     MY : Byte;               {Mouse absolute Y position}
  1298.     SaveMX : Byte;           {Saved mouse state}
  1299.     SaveMY : Byte;
  1300.     SaveMXL : Byte;
  1301.     SaveMXH : Byte;
  1302.     SaveMYL : Byte;
  1303.     SaveMYH : Byte;
  1304.     SaveWaitFor : Boolean;   {Saved WaitForButtonRelease variable}
  1305.     SaveMouseOn : Boolean;   {Was mouse cursor on at entry}
  1306.     SavePickMouseEnabled : Boolean; {Was pick enabled for mouse}
  1307.     Slid : Byte;             {Slider position}
  1308.     NewXnum : Xrefs;
  1309.     TmpXnum : Xrefs;
  1310.     {$ENDIF}
  1311.  
  1312.     PC : PickColorArray;
  1313.     HelpState : HelpStateRec;
  1314.     SaveFrameChars : FrameArray;
  1315.     HeaderStr : string[80];
  1316.   begin
  1317.     ShowHelpPrim := False;
  1318.     with Help^, Hdr, HelpState do begin
  1319.  
  1320.       {Validate request}
  1321.       if ID <> LongInt(HelpId) then
  1322.         Exit;
  1323.       if (Topic = 0) or (Topic > HighestTopic) then
  1324.         Exit;
  1325.  
  1326.       {Set colors and frame}
  1327.       AC[Attr1Toggle] := A[SpAtt1];
  1328.       AC[Attr2Toggle] := A[SpAtt2];
  1329.       AC[Attr3Toggle] := A[SpAtt3];
  1330.       AC[IndexMarker] := 0;
  1331.       AC[XrefToggle] := A[XsAttr];
  1332.       PC[WindowAttr] := A[TeAttr];
  1333.       PC[FrameAttr] := A[FrAttr];
  1334.       PC[HeaderAttr] := A[HeAttr];
  1335.       PC[SelectAttr] := A[XsAttr];
  1336.       PC[AltNormal] := A[TeAttr];
  1337.       PC[AltHigh] := A[XsAttr];
  1338.       SaveFrameChars := FrameChars;
  1339.  
  1340.       {Get help text into memory and initialize pointer to it}
  1341.       if not LoadHelp(Help, Topic) then
  1342.         Exit;
  1343.       {Scan help text to find page boundaries and xref markers}
  1344.       InitTopic(Help, HelpState);
  1345.  
  1346.       {Validate the page in case window was resized}
  1347.       if Page > Pcnt then {!!.06}
  1348.         Page := Pcnt;
  1349.  
  1350.       Pnum := Page;
  1351.       ShowMoreNow := ShowMore and (Pcnt > 1);
  1352.       HelpOnScreen := True;
  1353.  
  1354.       {Amount of space allocated for index buffers}
  1355.       SizeAlloc1 := 0;
  1356.       SizeAlloc2 := 0;
  1357.       HaveBuffers := False;
  1358.  
  1359.       {$IFDEF UseMouse}
  1360.       SaveMouseOn := MouseCursorOn;
  1361.       if SaveMouseOn then
  1362.         HideMouse;
  1363.       {$ENDIF}
  1364.  
  1365.       {Display window}
  1366.       FrameChars := Frame;
  1367.       GetHeaderString(Help, Topic, HeaderStr);
  1368.       if not MakeWindow(W,
  1369.         ColH, RowH,
  1370.         ColH+Width-1-2*FrameDelta[ShowFrame], RowH+Height-1,
  1371.         ShowFrame, True, False,
  1372.         A[TeAttr], A[FrAttr], A[HeAttr],
  1373.         HeaderStr) then
  1374.         goto ExitPoint;
  1375.       if not DisplayWindow(W) then
  1376.         goto ExitPoint;
  1377.       if HideCursor then
  1378.         HiddenCursor;
  1379.  
  1380.       {$IFDEF UseMouse}
  1381.       if HelpMouseEnabled then
  1382.         with WindowP(W)^, Draw do begin
  1383.           {Save current mouse parameters}
  1384.           SaveMX := MouseWhereX;
  1385.           SaveMY := MouseWhereY;
  1386.           SaveMXL := MouseXLo+1;
  1387.           SaveMXH := MouseXHi;
  1388.           SaveMYL := MouseYLo+1;
  1389.           SaveMYH := MouseYHi;
  1390.           SaveWaitFor := WaitForButtonRelease;
  1391.           {Set new mouse parameters}
  1392.           WaitForButtonRelease := True;
  1393.           UpdateMouseFrame(Help, HelpState);
  1394.           {Position to top left corner of window}
  1395.           MouseGoToXY(1, 1);
  1396.         end;
  1397.       {$ENDIF}
  1398.  
  1399.       {Allow user to browse help}
  1400.       Done := False;
  1401.       repeat
  1402.  
  1403.         {Draw a new help page if necessary}
  1404.         if Pnum <> Lnum then begin
  1405.           if XrefNum = 0 then
  1406.             Xnum := FirstXref(HelpState)
  1407.           else begin
  1408.             {Validate the Xref in case page size was changed}
  1409.             if X[XrefNum].Page <> Pnum then           {!!.06}
  1410.               XrefNum := FirstXref(HelpState);
  1411.             Xnum := XrefNum;
  1412.             XrefNum := 0;
  1413.           end;
  1414.           DrawPage(Help, HelpState);
  1415.           if ShowMoreNow then
  1416.             ShowPrompt(Help, HelpState);
  1417.           {$IFDEF UseMouse}
  1418.           if HelpMouseEnabled then
  1419.             if MouseScrollNow then
  1420.               {Draw slider}
  1421.               with WindowP(W)^.Draw do begin
  1422.                 Slid := SliderPos(HelpState);
  1423.                 if Lslid <> 0 then
  1424.                   FastWrite(Frame[Vert], Lslid, XH1, FAttr);
  1425.                 FastWrite(ScrollMark, Slid, XH1, FAttr);
  1426.                 Lslid := Slid;
  1427.               end;
  1428.           {$ENDIF}
  1429.           Lnum := Pnum;
  1430.         end;
  1431.  
  1432.         {$IFDEF UseMouse}
  1433.         if HelpMouseEnabled then
  1434.           ShowMouse;
  1435.         {$ENDIF}
  1436.  
  1437.         HelpCmdNum := GetCommand(HelpKeySet, HelpKeyPtr, ChWord);
  1438.  
  1439.         {$IFDEF UseMouse}
  1440.         if HelpMouseEnabled then
  1441.           HideMouse;
  1442.         {$ENDIF}
  1443.  
  1444.         case HelpCmdNum of
  1445.  
  1446.           {Commands to move xref highlight}
  1447.           HKSUp :
  1448.             IncVertXref(Help, HelpState, -1);
  1449.           HKSDown :
  1450.             IncVertXref(Help, HelpState, +1);
  1451.           HKSLeft :
  1452.             IncXref(Help, HelpState, -1);
  1453.           HKSRight :
  1454.             IncXref(Help, HelpState, +1);
  1455.  
  1456.           {Commands to select another page of help}
  1457.           HKSPgUp :
  1458.             if Pnum > 1 then
  1459.               Dec(Pnum);
  1460.           HKSPgDn :
  1461.             if Pnum < Pcnt then
  1462.               Inc(Pnum);
  1463.           HKSHome :
  1464.             Pnum := 1;
  1465.           HKSEnd :
  1466.             Pnum := Pcnt;
  1467.  
  1468.           {$IFDEF UseMouse}
  1469.           {Mouse probe}
  1470.           HKSProbe :
  1471.             if HelpMouseEnabled then
  1472.               with WindowP(W)^, Draw do begin
  1473.                 {Get absolute mouse coordinate}
  1474.                 MX := MouseXLo+MouseKeyWordX;
  1475.                 MY := MouseYLo+MouseKeyWordY;
  1476.                 if (MX = XL1) and (MY = YL1) then begin
  1477.                   {Request for previous help topic}
  1478.                   if St <> Sb then
  1479.                     goto LoadPrevTopic;
  1480.                 end else if MouseScrollNow and (MX = XH1) then begin
  1481.                   {In the scroll bar region}
  1482.                   if MY = YL1 then begin
  1483.                     {Move up one page}
  1484.                     if Pnum > 1 then
  1485.                       Dec(Pnum);
  1486.                   end else if MY = YH1 then begin
  1487.                     {Move down one page}
  1488.                     if Pnum < Pcnt then
  1489.                       Inc(Pnum);
  1490.                   end else if MY <> Lslid then begin
  1491.                     {Move proportional to mouse position}
  1492.                     Pnum := 1+((MY-YL)*(Pcnt-1)) div (YH-YL);
  1493.                     {Assure the slider moves at least one notch in the right direction}
  1494.                     if MY > Lslid then
  1495.                       while (SliderPos(HelpState) <= MY) and (Pnum < Pcnt) do
  1496.                         Inc(Pnum)
  1497.                     else if MY < Lslid then
  1498.                       while (SliderPos(HelpState) >= MY) and (Pnum > 1) do
  1499.                         Dec(Pnum);
  1500.                   end;
  1501.                 end else if (MY >= YL) and (MY <= YH) then begin
  1502.                   {In the active pick region, convert to window relative}
  1503.                   Dec(MX, XL-1);
  1504.                   Dec(MY, YL-1);
  1505.                   {Select another xref if possible}
  1506.                   NewXnum := IsXRef(Help, HelpState, MX, MY);
  1507.                   if NewXnum <> 0 then
  1508.                     if NewXnum = Xnum then
  1509.                       {Second click on item, select it}
  1510.                       goto SelectATopic
  1511.                     else begin
  1512.                       {Move highlight to item}
  1513.                       TmpXnum := Xnum;
  1514.                       Xnum := NewXnum;
  1515.                       DrawXref(Help, HelpState, TmpXnum);
  1516.                       DrawXref(Help, HelpState, Xnum);
  1517.                     end;
  1518.                 end;
  1519.               end;
  1520.           {$ENDIF}
  1521.  
  1522.           {Commands to exit help or select another topic}
  1523.           HKSExit, HKSUser0..HKSUser3 :
  1524.             begin
  1525.               {Save current help topic and page}
  1526.               PushStack(Help, Topic, Pnum, Xnum);
  1527.               Done := True;
  1528.               ShowHelpPrim := True;
  1529.             end;
  1530.           HKSSelect :
  1531.             if Xnum <> 0 then begin
  1532. SelectATopic:
  1533.               {Save current help topic and page}
  1534.               PushStack(Help, Topic, Pnum, Xnum);
  1535.               Topic := X[Xnum].Topic;
  1536.  
  1537.               {Show the help index if a special topic number is specified}
  1538.               if Topic = IndexXrefTopic then
  1539.                 GoTo ShowIndex;
  1540.  
  1541.               Done := not LoadNewTopic(Help, HelpState, Topic);
  1542.               if not Done then begin
  1543.                 ShowMoreNow := ShowMore and (Pcnt > 1);
  1544.                 {$IFDEF UseMouse}
  1545.                 if HelpMouseEnabled then
  1546.                   UpdateMouseFrame(Help, HelpState);
  1547.                 {$ENDIF}
  1548.               end;
  1549.             end;
  1550.           HKSPrev :
  1551.             if St <> Sb then begin
  1552. LoadPrevTopic:
  1553.               {Restore previous displayed topic and page}
  1554.               PopStack(Help, Topic, Page, XrefNum);
  1555.               Done := not LoadNewTopic(Help, HelpState, Topic);
  1556.               if not Done then begin
  1557.                 Pnum := Page;
  1558.                 ShowMoreNow := ShowMore and (Pcnt > 1);
  1559.                 {$IFDEF UseMouse}
  1560.                 if HelpMouseEnabled then
  1561.                   UpdateMouseFrame(Help, HelpState);
  1562.                 {$ENDIF}
  1563.               end;
  1564.             end;
  1565.           HKSIndex :
  1566. ShowIndex:
  1567.             if (NamedTopics > 0) and (PickCs > 0) then begin
  1568.               {Find keystroke for previous help topic}
  1569.               GetKeysForCommand(HelpKeySet, HKSPrev, NumKeys, Key1, Key2);
  1570.               if NumKeys <> 0 then
  1571.                 {Temporarily add command to pick set for previous help topic}
  1572.                 if AddPickCommand(PKSUser3, NumKeys, Key1, Key2) then begin
  1573.                   if not HaveBuffers then
  1574.                     {Allocate and read index tables}
  1575.                     if GetBuffer(Help, PBuff,
  1576.                       SizeOf(HelpHeader),
  1577.                       HighestTopic*NameSize, SizeAlloc1) then
  1578.                       if GetBuffer(Help, TBuff,
  1579.                         SizeOf(HelpHeader)+
  1580.                         LongInt(HighestTopic)*(NameSize+SizeOf(HelpIndexRec)),
  1581.                         HighestTopic*SizeOf(Word), SizeAlloc2) then
  1582.                         HaveBuffers := True;
  1583.                   if HaveBuffers then begin
  1584.                     Choice := 1;
  1585.                     Row := 1;
  1586.                     SavePickMatrix := PickMatrix;
  1587.                     PickMatrix := PickCs;
  1588.                     NSize := NameSize;
  1589.                     {Temporarily disable help within pick}
  1590.                     SavePickHelp := PickHelpPtr; {!! 5.07}
  1591.                     PickHelpPtr := nil;          {!! 5.07}
  1592.  
  1593.                     {$IFDEF UseMouse}
  1594.                     if HelpMouseEnabled then begin
  1595.                       {Assure mouse is also on in TPPICK}
  1596.                       SavePickMouseEnabled := PickMouseEnabled;
  1597.                       if not PickMouseEnabled then
  1598.                         EnablePickMouse;
  1599.                     end;
  1600.                     {$ENDIF}
  1601.  
  1602.                     FrameHelp(Help, HelpState, HelpTitle);
  1603.                     ClrScr;
  1604.                     FillPickWindow(W, @SendHelpName, NamedTopics,
  1605.                       PC, Choice, Row);
  1606.                     PickBar(W, @SendHelpName, NamedTopics, PC, False,
  1607.                       Choice, Row);
  1608.                     PickMatrix := SavePickMatrix;
  1609.                     PickHelpPtr := SavePickHelp; {!! 5.07}
  1610.  
  1611.                     {$IFDEF UseMouse}
  1612.                     if HelpMouseEnabled then
  1613.                       if not SavePickMouseEnabled then begin
  1614.                         {Assure mouse is now off in TPPICK}
  1615.                         DisablePickMouse;
  1616.                         {But that it stays on for TPHELP}
  1617.                         EnableHelpMouse;
  1618.                       end;
  1619.                     {$ENDIF}
  1620.  
  1621.                     case PickCmdNum of
  1622.                       PKSSelect :
  1623.                         begin
  1624.                           {Save current help topic and page}
  1625.                           PushStack(Help, Topic, Pnum, Xnum);
  1626.                           {Prepare to display new topic}
  1627.                           Topic := TBuff^[Choice];
  1628.                           Done := not LoadNewTopic(Help, HelpState, Topic);
  1629.                           if not Done then begin
  1630.                             ShowMoreNow := ShowMore and (Pcnt > 1);
  1631.                             {$IFDEF UseMouse}
  1632.                             if HelpMouseEnabled then
  1633.                               UpdateMouseFrame(Help, HelpState);
  1634.                             {$ENDIF}
  1635.                           end;
  1636.                         end;
  1637.                       PKSUser3 :
  1638.                         begin
  1639.                           {Redisplay previous topic}
  1640.                           GetHeaderString(Help, Topic, HeaderStr);
  1641.                           FrameHelp(Help, HelpState, HeaderStr);
  1642.                           Lnum := 0;
  1643.                           XrefNum := Xnum;
  1644.                           if not Done then begin
  1645.                             ShowMoreNow := ShowMore and (Pcnt > 1);
  1646.                             {$IFDEF UseMouse}
  1647.                             if HelpMouseEnabled then {!!}
  1648.                               UpdateMouseFrame(Help, HelpState);
  1649.                             {$ENDIF}
  1650.                           end;
  1651.                         end;
  1652.                     else
  1653.                       Done := True;
  1654.                       ShowHelpPrim := True;
  1655.                     end;
  1656.                     {Deactivate special added command}
  1657.                     if AddPickCommand(PKSNone, NumKeys, Key1, Key2) then ;
  1658.                   end;
  1659.                 end;
  1660.             end;
  1661.  
  1662.         end;
  1663.       until Done;
  1664.  
  1665.       {Deallocate index buffer space}
  1666.       if SizeAlloc1 <> 0 then
  1667.         FreeMem(PBuff, SizeAlloc1);
  1668.       if SizeAlloc2 <> 0 then
  1669.         FreeMem(TBuff, SizeAlloc2);
  1670.       {Restore the screen}
  1671.       DisposeWindow(EraseTopWindow);
  1672.  
  1673.       {$IFDEF UseMouse}
  1674.       if HelpMouseEnabled then begin
  1675.         {Restore mouse position and window}
  1676.         MouseWindow(SaveMXL, SaveMYL, SaveMXH, SaveMYH);
  1677.         MouseGoToXY(SaveMX, SaveMY);
  1678.         WaitForButtonRelease := SaveWaitFor;
  1679.       end;
  1680.       if SaveMouseOn then
  1681.         ShowMouse;
  1682.       {$ENDIF}
  1683.  
  1684. ExitPoint:
  1685.       FrameChars := SaveFrameChars;
  1686.       HelpOnScreen := False;
  1687.     end;
  1688.   end;
  1689.  
  1690.   function ShowHelp(Help : HelpPtr; Topic : Word) : Boolean;
  1691.     {-Display help screen, returning true if successful}
  1692.   begin
  1693.     ShowHelp := ShowHelpPrim(Help, Topic, 1, 0);
  1694.   end;
  1695.  
  1696.   function ShowPrevHelp(Help : HelpPtr) : Boolean;
  1697.     {-Display help screen for topic most recently selected}
  1698.   var
  1699.     Topic : Word;
  1700.     Page : Pages;
  1701.     XrefNum : Xrefs;
  1702.   begin
  1703.     ShowPrevHelp := False;
  1704.     with Help^ do
  1705.       if St <> Sb then begin
  1706.         PopStack(Help, Topic, Page, XrefNum);
  1707.         ShowPrevHelp := ShowHelpPrim(Help, Topic, Page, XrefNum);
  1708.       end;
  1709.   end;
  1710.  
  1711.   function FindHelp(Help : HelpPtr; Name : string; MatchFunc : Pointer) : Word;
  1712.     {-Return topic number of help with specified Name, 0 if not found}
  1713.   label
  1714.     ExitPoint;
  1715.   var
  1716.     P : CharArrayPtr;
  1717.     NP : StringPtr;
  1718.     SizeAlloc : Word;
  1719.     I : Word;
  1720.  
  1721.     function CallMatch(S1, S2 : string) : Boolean;
  1722.       {-Call routine pointed to by MatchFunc}
  1723.     inline($FF/$5E/<MatchFunc); {Call dword ptr [bp+<MatchFunc]}
  1724.  
  1725.   begin
  1726.     FindHelp := 0;
  1727.     if MatchFunc = nil then
  1728.       MatchFunc := @Match;
  1729.     with Help^, Hdr do begin
  1730.       {Validate help structure}
  1731.       if ID <> LongInt(HelpId) then
  1732.         Exit;
  1733.       if GetBuffer(Help, P, SizeOf(HelpHeader), HighestTopic*NameSize, SizeAlloc) then begin
  1734.         {Match the name}
  1735.         NP := StringPtr(P);
  1736.         for I := 1 to HighestTopic do
  1737.           if CallMatch(NP^, Name) then begin
  1738.             FindHelp := I;
  1739.             goto ExitPoint;
  1740.           end else
  1741.             Inc(SO(NP).O, NameSize);
  1742.       end;
  1743.     end;
  1744. ExitPoint:
  1745.     if SizeAlloc <> 0 then
  1746.       FreeMem(P, SizeAlloc);
  1747.   end;
  1748.  
  1749.   function WordChar(Ch : Char) : Boolean;
  1750.     {-Return true if Ch is a character in a word}
  1751.   begin
  1752.     case Upcase(Ch) of
  1753.       'A'..'Z', '_', '0'..'9' : WordChar := True;
  1754.     else
  1755.       WordChar := False;
  1756.     end;
  1757.   end;
  1758.  
  1759.   function ScreenHelp(Help : HelpPtr; XPos, YPos : Byte;
  1760.                       ScanBack : Boolean; MatchFunc : Pointer) : Word;
  1761.     {-Return topic matching screen contents at position (XPos,YPos) or 0 if none}
  1762.   var
  1763.     Bpos : Byte;
  1764.     EPos : Byte;
  1765.     SName : string;
  1766.   begin
  1767.     ScreenHelp := 0;
  1768.  
  1769.     {Read entire screen row at YPos}
  1770.     FastRead(ScreenWidth, YPos, 1, SName);
  1771.  
  1772.     EPos := XPos;
  1773.     if ScanBack then
  1774.       {Back up until in a word}
  1775.       while (EPos > 1) and not WordChar(SName[EPos]) do
  1776.         Dec(EPos);
  1777.  
  1778.     {Get out if no word available}
  1779.     if not WordChar(SName[EPos]) then
  1780.       Exit;
  1781.  
  1782.     {Find beginning of word}
  1783.     Bpos := EPos;
  1784.     while (Bpos > 0) and WordChar(SName[Bpos]) do
  1785.       Dec(Bpos);
  1786.  
  1787.     {Find end of word}
  1788.     while (EPos <= Length(SName)) and WordChar(SName[EPos]) do
  1789.       Inc(EPos);
  1790.  
  1791.     {Search for help by that name}
  1792.     SName := Copy(SName, Bpos+1, EPos-Bpos-1);
  1793.     ScreenHelp := FindHelp(Help, SName, MatchFunc);
  1794.   end;
  1795.  
  1796.   function PickHelp(Help : HelpPtr; XLow, YLow, YHigh, PickCols : Byte) : Word;
  1797.     {-Display help pick list, returning Topic number, or 0 for none}
  1798.   var
  1799.     SizeAlloc1 : Word;
  1800.     SizeAlloc2 : Word;
  1801.     Choice : Word;
  1802.     XHigh : Byte;
  1803.     SaveFrameChars : FrameArray;
  1804.     SavePickMatrix : Byte;
  1805.     SavePickMouseEnabled : Boolean;
  1806.     PC : PickColorArray;
  1807.   begin
  1808.     PickHelp := 0;
  1809.     with Help^, Hdr do begin
  1810.       {Validate help structure}
  1811.       if ID <> LongInt(HelpId) then
  1812.         Exit;
  1813.       if GetBuffer(Help, PBuff,
  1814.         SizeOf(HelpHeader),
  1815.         HighestTopic*NameSize, SizeAlloc1) then
  1816.         if GetBuffer(Help, TBuff,
  1817.           SizeOf(HelpHeader)+
  1818.           LongInt(HighestTopic)*(NameSize+SizeOf(HelpIndexRec)),
  1819.           HighestTopic*SizeOf(Word), SizeAlloc2) then begin
  1820.           {Set colors and frame}
  1821.           PC[WindowAttr] := A[TeAttr];
  1822.           PC[FrameAttr] := A[FrAttr];
  1823.           PC[HeaderAttr] := A[HeAttr];
  1824.           PC[SelectAttr] := A[XsAttr];
  1825.           PC[AltNormal] := A[TeAttr];
  1826.           PC[AltHigh] := A[XsAttr];
  1827.           SaveFrameChars := FrameChars;
  1828.           SavePickMatrix := TpPick.PickMatrix;
  1829.  
  1830.           {Set up global with NameSize}
  1831.           NSize := NameSize;
  1832.  
  1833.           {Choose the window width}
  1834.           XHigh := XLow+PickCols*(NSize+1)+1;
  1835.           if XHigh > ScreenWidth then
  1836.             XHigh := ScreenWidth;
  1837.  
  1838.           FrameChars := Frame;
  1839.           TpPick.PickMatrix := PickCols;
  1840.           Choice := 1;
  1841.  
  1842.           {$IFDEF UseMouse}
  1843.           if HelpMouseEnabled then begin
  1844.             {Assure mouse is also on in TPPICK}
  1845.             SavePickMouseEnabled := PickMouseEnabled;
  1846.             if not PickMouseEnabled then
  1847.               EnablePickMouse;
  1848.           end;
  1849.           {$ENDIF}
  1850.  
  1851.           {Pick from list}
  1852.           if PickWindow(@SendHelpName, NamedTopics,
  1853.             XLow, YLow, XHigh, YHigh, UseHelpFrame,
  1854.             PC, HelpTitle, Choice) then
  1855.             if PickCmdNum = PKSSelect then
  1856.               PickHelp := TBuff^[Choice];
  1857.  
  1858.           {$IFDEF UseMouse}
  1859.           if HelpMouseEnabled then
  1860.             if not SavePickMouseEnabled then begin
  1861.               {Assure mouse is now off in TPPICK}
  1862.               DisablePickMouse;
  1863.               {But that it stays on for TPHELP}
  1864.               EnableHelpMouse;
  1865.             end;
  1866.           {$ENDIF}
  1867.           FrameChars := SaveFrameChars;
  1868.           TpPick.PickMatrix := SavePickMatrix;
  1869.         end;
  1870.     end;
  1871.  
  1872.     if SizeAlloc1 <> 0 then
  1873.       FreeMem(PBuff, SizeAlloc1);
  1874.     if SizeAlloc2 <> 0 then
  1875.       FreeMem(TBuff, SizeAlloc2);
  1876.   end;
  1877.  
  1878.   function AddHelpCommand(Cmd : HKtype; NumKeys : Byte; Key1, Key2 : Word) : Boolean;
  1879.     {-Add a new command key assignment or change an existing one}
  1880.   begin
  1881.     AddHelpCommand := AddCommandPrim(HelpKeySet, HelpKeyMax, Cmd, NumKeys, Key1, Key2);
  1882.   end;
  1883.  
  1884.   procedure DisableHelpIndex;
  1885.     {-Disable the F1 help index inside of a help screen}
  1886.   var
  1887.     Junk : Boolean;
  1888.   begin
  1889.     Junk := AddHelpCommand(HKSNone, 1, $3B00, 0);
  1890.     {$IFDEF UseMouse}
  1891.     Junk := AddHelpCommand(HKSNone, 1, $ED00, 0);
  1892.     {$ENDIF}
  1893.     HelpIndexDisabled := True;
  1894.   end;
  1895.  
  1896.   procedure EnableHelpIndex;
  1897.     {-Enable the F1 help index inside of a help screen}
  1898.   var
  1899.     Junk : Boolean;
  1900.   begin
  1901.     Junk := AddHelpCommand(HKSIndex, 1, $3B00, 0);
  1902.     {$IFDEF UseMouse}
  1903.     Junk := AddHelpCommand(HKSIndex, 1, $ED00, 0);
  1904.     {$ENDIF}
  1905.     HelpIndexDisabled := False;
  1906.   end;
  1907.  
  1908.   {$IFDEF UseMouse}
  1909.   procedure EnableHelpMouse;
  1910.     {-Enable mouse control of the help system}
  1911.   begin
  1912.     if MouseInstalled then begin
  1913.       HelpKeyPtr := @TpMouse.ReadKeyOrButton;
  1914.       EnableEventHandling;
  1915.       HelpMouseEnabled := True;
  1916.     end;
  1917.   end;
  1918.  
  1919.   procedure DisableHelpMouse;
  1920.     {-Disable mouse control of the help system}
  1921.   begin
  1922.     if HelpMouseEnabled then begin
  1923.       HelpKeyPtr := @ReadKeyWord;
  1924.       DisableEventHandling;
  1925.       HelpMouseEnabled := False;
  1926.     end;
  1927.   end;
  1928.   {$ENDIF}
  1929.  
  1930. begin
  1931.   HelpKeyPtr := @ReadKeyWord;
  1932.   HelpOnScreen := False;
  1933.   {$IFDEF UseMouse}
  1934.   HelpMouseEnabled := False;
  1935.   {$ENDIF}
  1936. end.
  1937.