home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 16 / 16.iso / w / w048 / 2.ddi / MSSRC.ARC / MSMACRO.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1987-12-21  |  26.3 KB  |  868 lines

  1. {                            MSMACRO.PAS
  2.                                MS 4.0
  3.                 Copyright (c) 1985, 87 by Borland International, Inc.         }
  4.  
  5. {$I msdirect.inc}
  6.  
  7. unit MsMacro;
  8.   {-Perform macro operations}
  9.  
  10. interface
  11.  
  12. uses
  13.   Crt,                       {Basic video operations - standard unit}
  14.   Dos,                       {DOS interface - standard unit}
  15.   Errors,                    {Runtime error handler}
  16.   MsVars,                    {Global types and declarations}
  17.   MsScrn1,                   {Fast screen writing routines}
  18.   MsString,                  {String primitives}
  19.   MsPtrOp,                   {Pointer primitives}
  20.   EscSeq,                    {Returns text string for extended scan codes}
  21.   MsCmds,                    {Maps keystrokes to commands}
  22.   Int24,                     {DOS critical error handler}
  23.   Message,                   {Message system}
  24.   MsUser,                    {User keyboard input, line edit, error report, help}
  25.   MsMemOp,                   {Text buffer allocation and deallocation}
  26.   MsBack,                    {Background processes}
  27.   MsScrn2,                   {Editor screen updating}
  28.   MsMenu,                    {Pulldown and custom menu system}
  29.   MsDir,                     {Popup directory picker}
  30.   MsEdit,                    {Basic editing commands}
  31.   MsText,                    {Text processing commands}
  32.   MsFile;                    {File I/O routines}
  33.  
  34. procedure EdInsertMacro(MacNum, Ntimes : Integer);
  35.   {-Put macro characters into typeahead buffer}
  36.  
  37. procedure EdGetMacroNumber(Msgno : Integer; var Macronum : Integer);
  38.   {-Use a menu to choose a macro by name or number}
  39.  
  40. procedure EdReadMacroFile(Fname : Filepath);
  41.   {-Read an existing macro file}
  42.  
  43. procedure EdWriteMacroFile(Fname : Filepath);
  44.   {-Write a new macro file}
  45.  
  46. procedure EdToggleMacroRecord;
  47.   {-Toggle whether keystrokes are being recorded for use in macros}
  48.  
  49. procedure EdPromptMacroInsert;
  50.   {-Prompt for and insert a macro once}
  51.  
  52. procedure EdEditKeyWindow(msg : VarString; Xmin, Ymin, Xmax, Ymax, Maxlen : Integer; var Keys : MacroString);
  53.   {-Set up a window, and edit the keystring}
  54.  
  55. procedure EdEditMacro;
  56.   {-Edit a macro}
  57.  
  58.   {==========================================================================}
  59.  
  60. implementation
  61.  
  62.  
  63.   procedure EdInsertMacro(MacNum, Ntimes : Integer);
  64.     {-Put macro characters into typeahead buffer}
  65.   var
  66.     Bufsize, MaxTimes : Integer;
  67.     I : Integer;
  68.     St, StNum : VarString;
  69.     Mac : MacroString;
  70.  
  71.   begin                      {EdInsertMacro}
  72.  
  73.     Mac := Macrokeys[MacNum];
  74.     if Length(Mac) = 0 then
  75.       Exit;
  76.  
  77.     {Cannot playback scrap macro while recording}
  78.     if Recording and (MacNum = 0) then begin
  79.       EdErrormsg(42);
  80.       {Clear scrap macro}
  81.       EdClearString(Macrokeys[0]);
  82.       Exit;
  83.     end;
  84.  
  85.     {Get maximum number of insertions that will fit into typeahead buffer}
  86.     Bufsize := DefTypeahead;
  87.     MaxTimes := Bufsize div Length(Mac);
  88.  
  89.     if Ntimes <= 0 then begin
  90.       {Prompt for number of times if undefined}
  91.       Str(MaxTimes, St);
  92.       EdClearString(StNum);
  93.       EdAskfor(EdGetMessage(314)+St+') ', 10, EdYcenterWindow(3), 47, StNum);
  94.       if Abortcmd then
  95.         Exit;
  96.       EdString2integer(StNum, Ntimes);
  97.       if (Ntimes = 0) then
  98.         Exit;
  99.     end;
  100.  
  101.     {Apply ceiling to number of insertions}
  102.     if Ntimes > MaxTimes then
  103.       Ntimes := MaxTimes;
  104.  
  105.     {Put keystrokes into typeahead buffer}
  106.     for I := 1 to Ntimes do
  107.       EdUserPush(Mac);
  108.  
  109.   end;                       {EdInsertmacro}
  110.  
  111.   procedure EdGetMacroNumber(Msgno : Integer; var Macronum : Integer);
  112.     {-Use a menu to choose a macro by name or number}
  113.   var
  114.     Menu : CustomMenuRec;
  115.  
  116.     function EdBuildMessages(var Menu : CustomMenuRec) : Boolean;
  117.       {-Build the message table for the menu}
  118.     var
  119.       Item : Integer;
  120.       S : VarString;
  121.  
  122.     begin                    {EdBuildMessages}
  123.       EdBuildMessages := False;
  124.       with Menu do begin
  125.         if EdMemAvail(Succ(MaxChoice) shl 2, FreeListTemp) then
  126.           {Get the pointers}
  127.           GetMem(Messages, Succ(MaxChoice) shl 2)
  128.         else
  129.           Exit;
  130.  
  131.         {Get the string space and fill in the items}
  132.         for Item := MinChoice to MaxChoice do begin
  133.           Str(Item, S);
  134.           if EdPtrIsNil(Macronames[Item]) then
  135.             S := S+EdGetMessage(384)
  136.           else begin
  137.             S := S+Blank+Macronames[Item]^;
  138.             if Length(S) > 50 then
  139.               S := Copy(S, 1, 50);
  140.           end;
  141.           if EdMemAvail(Succ(Length(S)), FreeListTemp) then
  142.             GetMem(Messages^[Item], Succ(Length(S)))
  143.           else
  144.             Exit;
  145.           Messages^[Item]^ := S;
  146.         end;
  147.       end;
  148.       EdBuildMessages := True;
  149.     end;                     {EdBuildMessages}
  150.  
  151.   begin                      {EdGetMacroNumber}
  152.  
  153.     {Initialize the menu}
  154.     with Menu do begin
  155.       Xmin := 26;
  156.       Ymin := 13;
  157.       MessageNum := Msgno;
  158.       PromptNum := 383;
  159.       MinChoice := 0;
  160.       MaxChoice := MaxMacro;
  161.       InitChoice := 0;
  162.       CmdSet := NumCmdSet;
  163.       UseLetters := False;
  164.     end;
  165.     if not(EdBuildMessages(Menu)) then begin
  166.       EdErrormsg(35);
  167.       Exit;
  168.     end;
  169.  
  170.     {Get the menu choice}
  171.     EdGetCustomMenuChoice(Menu, Macronum);
  172.  
  173.     if Abortcmd then
  174.       Macronum := MaxInt;
  175.  
  176.   end;                       {EdGetMacroNumber}
  177.  
  178.   procedure EdGetMacroName(N : Integer);
  179.     {-Prompt for and store a macro name for macro n}
  180.   var
  181.     St : VarString;
  182.  
  183.     function EdGetNameSpace(Len : Byte) : Boolean;
  184.       {-Determine whether space available for a macro name}
  185.     var
  186.       Space : Boolean;
  187.  
  188.     begin                    {EdGetNameSpace}
  189.       Space := EdMemAvail(Succ(Len), FreeListPerm);
  190.       if not(Space) then
  191.         EdErrormsg(35);
  192.       EdGetNameSpace := Space;
  193.     end;                     {EdGetNameSpace}
  194.  
  195.   begin                      {EdGetMacroName}
  196.     if EdPtrIsNil(Macronames[N]) then
  197.       EdClearString(St)
  198.     else
  199.       St := Macronames[N]^;
  200.     EdAskfor(EdGetMessage(385), 10, 20, 53, St);
  201.     if Abortcmd then
  202.       Exit;
  203.  
  204.     {Store it on the heap}
  205.     if not(EdStringEmpty(St)) then
  206.       if EdPtrIsNil(Macronames[N]) then begin
  207.         {No space allocated yet}
  208.         if EdGetNameSpace(Length(St)) then
  209.           GetMem(Macronames[N], Succ(Length(St)));
  210.         Move(St, Macronames[N]^, Succ(Length(St)));
  211.       end else if Ord(Macronames[N]^[0]) < Length(St) then begin
  212.         {Space too small, get a larger block}
  213.         FreeMem(Macronames[N], Succ(Ord(Macronames[N]^[0])));
  214.         if EdGetNameSpace(Length(St)) then
  215.           GetMem(Macronames[N], Succ(Length(St)));
  216.         Move(St, Macronames[N]^, Succ(Length(St)));
  217.       end else
  218.         {Adequate space, overwrite it}
  219.         Move(St, Macronames[N]^, Succ(Length(St)));
  220.   end;                       {EdGetMacroName}
  221.  
  222.   procedure EdReadMacroFile(Fname : Filepath);
  223.     {-Read an existing macro file}
  224.   label
  225.     ExitPoint;
  226.   var
  227.     Mnum, BytesRead : Integer;
  228.     Mname : VarString;
  229.     F : file;
  230.  
  231.     function EdGetNameSpace(Len : Byte) : Boolean;
  232.       {-Check that there is space for a macro name}
  233.     var
  234.       Space : Boolean;
  235.  
  236.     begin                    {EdGetNameSpace}
  237.       Space := EdMemAvail(Succ(Len), FreeListPerm);
  238.       if not(Space) then
  239.         EdErrormsg(35);
  240.       EdGetNameSpace := Space;
  241.     end;                     {EdGetNameSpace}
  242.  
  243.   begin                      {EdReadMacroFile}
  244.  
  245.     if Abortcmd or EdStringEmpty(Fname) then
  246.       Exit;
  247.     Assign(F, Fname);
  248.     Reset(F, 1);
  249.     if EdFileerror then
  250.       Exit;
  251.  
  252.     {Check file signature}
  253.     EdBlockRead(F, Mname, Succ(Length(MacroSignature)), BytesRead);
  254.     if EdFileerror or (BytesRead <> Succ(Length(MacroSignature))) then
  255.       goto ExitPoint;
  256.     if Mname <> MacroSignature then begin
  257.       EdErrormsg(50);
  258.       goto ExitPoint;
  259.     end;
  260.  
  261.     for Mnum := 0 to MaxMacro do begin
  262.  
  263.       {Get the macro name}
  264.       EdBlockRead(F, Mname[0], 1, BytesRead);
  265.       if Goterror or (BytesRead <> 1) then
  266.         goto ExitPoint;
  267.  
  268.       if not(EdStringEmpty(Mname)) then begin
  269.         EdBlockRead(F, Mname[1], Length(Mname), BytesRead);
  270.         if Goterror or (BytesRead <> Length(Mname)) then
  271.           goto ExitPoint;
  272.         {Store it on the heap}
  273.         if EdPtrIsNil(Macronames[Mnum]) then begin
  274.           {No space allocated yet}
  275.           if not(EdGetNameSpace(Length(Mname))) then
  276.             goto ExitPoint;
  277.           GetMem(Macronames[Mnum], Succ(Length(Mname)));
  278.         end else if Length(Macronames[Mnum]^) < Length(Mname) then begin
  279.           {Space too small, get a larger block}
  280.           FreeMem(Macronames[Mnum], Succ(Length(Macronames[Mnum]^)));
  281.           if not(EdGetNameSpace(Length(Mname))) then
  282.             goto ExitPoint;
  283.           GetMem(Macronames[Mnum], Succ(Length(Mname)));
  284.         end;
  285.         Move(Mname, Macronames[Mnum]^, Succ(Length(Mname)));
  286.       end else if EdPtrNotNil(Macronames[Mnum]) then
  287.         {Clear the name}
  288.         Macronames[Mnum]^[0] := Null;
  289.  
  290.       {Read the macro characters}
  291.       EdBlockRead(F, Macrokeys[Mnum] [0], 1, BytesRead);
  292.       if Goterror or (BytesRead <> 1) then
  293.         goto ExitPoint;
  294.  
  295.       if not(EdStringEmpty(Macrokeys[Mnum])) then begin
  296.         EdBlockRead(F, Macrokeys[Mnum] [1], Length(Macrokeys[Mnum]), BytesRead);
  297.         if Goterror or (BytesRead <> Length(Macrokeys[Mnum])) then
  298.           goto ExitPoint;
  299.       end;
  300.  
  301.     end;
  302.  
  303. ExitPoint:
  304.     Close(F);
  305.     if EdFileerror then
  306.       ;
  307.   end;                       {EdReadMacroFile}
  308.  
  309.   procedure EdWriteMacroFile(Fname : Filepath);
  310.     {-Write a new macro file}
  311.   label
  312.     ExitPoint;
  313.   var
  314.     Mnum : Integer;
  315.     F : file;
  316.     Nothing : string[1];
  317.  
  318.   begin                      {EdWriteMacroFile}
  319.  
  320.     if Abortcmd or EdStringEmpty(Fname) then
  321.       Exit;
  322.  
  323.     if EdExistFile(Fname) then begin
  324.       {Prompt to overwrite}
  325.       if not(EdYesNo(EdGetMessage(319))) then
  326.         Exit;
  327.       if Abortcmd then
  328.         Exit;
  329.     end;
  330.  
  331.     Assign(F, Fname);
  332.     Rewrite(F, 1);
  333.     if EdFileerror then
  334.       Exit;
  335.  
  336.     {Write file signature}
  337.     EdBlockWrite(F, MacroSignature, Succ(Length(MacroSignature)));
  338.     if Goterror then
  339.       goto ExitPoint;
  340.  
  341.     EdClearString(Nothing);
  342.  
  343.     for Mnum := 0 to MaxMacro do begin
  344.       {Write the name}
  345.       if EdPtrNotNil(Macronames[Mnum]) then begin
  346.         {Write the macro name}
  347.         EdBlockWrite(F, Macronames[Mnum]^, Succ(Length(Macronames[Mnum]^)));
  348.         if Goterror then
  349.           goto ExitPoint;
  350.       end else begin
  351.         {Write an empty string}
  352.         EdBlockWrite(F, Nothing, 1);
  353.         if Goterror then
  354.           goto ExitPoint;
  355.       end;
  356.  
  357.       {Write the macro keys}
  358.       EdBlockWrite(F, Macrokeys[Mnum], Succ(Length(Macrokeys[Mnum])));
  359.       if Goterror then
  360.         goto ExitPoint;
  361.     end;
  362.  
  363. ExitPoint:
  364.     Close(F);
  365.     if EdFileerror then
  366.       ;
  367.   end;                       {EdWriteMacroFile}
  368.  
  369.   procedure EdToggleMacroRecord;
  370.     {-Toggle whether keystrokes are being recorded for use in macros}
  371.   var
  372.     N : Integer;
  373.  
  374.     procedure EdRemoveToggleCommand(var M : MacroString);
  375.       {-Remove the keys which stopped the recording}
  376.  
  377.       function EdRemoved(Toggle : CommandString; var M : MacroString) : Boolean;
  378.         {-Return true if command Toggle is found and removed}
  379.       var
  380.         Cpos : Byte;
  381.  
  382.         function EdCmdFound(Lcmd : MacroString; Scmd : CommandString; P : Integer) : Boolean;
  383.           {-Return true if scmd is found at position p in lcmd}
  384.         var
  385.           Tcmd : CommandString;
  386.           Found : Boolean;
  387.           I : Integer;
  388.  
  389.         begin                {EdCmdFound}
  390.           if (Length(Scmd) = 0) or (P = 0) then
  391.             Found := False
  392.           else begin
  393.             {Get the substring which must match}
  394.             Tcmd := Copy(Lcmd, P, Length(Scmd));
  395.             Found := True;
  396.             {Scan the command keys checking for match}
  397.             I := 1;
  398.             while Found and (I <= Length(Scmd)) do begin
  399.               if (I > Length(Tcmd)) then
  400.                 Found := False
  401.               else if I = 1 then
  402.                 {Force exact match on first character}
  403.                 Found := (Scmd[I] = Tcmd[I])
  404.               else
  405.                 {Use control char equivalents on later characters}
  406.                 Found := (EdControlFilter(Scmd[I]) = EdControlFilter(Tcmd[I]));
  407.               Inc(I);
  408.             end;
  409.           end;
  410.           EdCmdFound := Found;
  411.         end;                 {EdCmdFound}
  412.  
  413.       begin                  {EdRemoved}
  414.         EdRemoved := False;
  415.         if Length(Toggle) > 0 then begin
  416.           Cpos := Succ(Length(M)-Length(Toggle));
  417.           while (Cpos > 0) and not(EdCmdFound(M, Toggle, Cpos)) do
  418.             Dec(Cpos);
  419.           if Cpos > 0 then begin
  420.             M := Copy(M, 1, Pred(Cpos));
  421.             EdRemoved := True;
  422.           end;
  423.         end;
  424.       end;                   {EdRemoved}
  425.  
  426.     begin                    {EdRemoveToggleCommand}
  427.       {Check the four ways macro recording could be turned off}
  428.       if EdRemoved(TogglePrime, M) then
  429.         Exit;
  430.       if EdRemoved(ToggleSecon, M) then
  431.         Exit;
  432.       if EdRemoved(MenuPrime, M) then
  433.         Exit;
  434.       if EdRemoved(MenuSecon, M) then
  435.         Exit;
  436.       EdClearString(M);
  437.     end;                     {EdRemoveToggleCommand}
  438.  
  439.   begin                      {EdToggleMacroRecord}
  440.     Recording := not(Recording);
  441.     if Recording then
  442.  
  443.       {Starting a recorded macro - reset scrap string}
  444.       EdClearString(Macrokeys[0])
  445.  
  446.     else begin
  447.  
  448.       {Remove the toggle command from the end of the macro string}
  449.       EdRemoveToggleCommand(Macrokeys[0]);
  450.  
  451.       {Get a macro number}
  452.       EdGetMacroNumber(316, N);
  453.       if Abortcmd or not(N in [0..MaxMacro]) then
  454.         Exit;
  455.  
  456.       {Get the macro name}
  457.       EdGetMacroName(N);
  458.       if Abortcmd then
  459.         Exit;
  460.  
  461.       {Store the macro}
  462.       if N <> 0 then
  463.         Macrokeys[N] := Macrokeys[0];
  464.  
  465.     end;
  466.   end;                       {EdToggleMacroRecord}
  467.  
  468.   procedure EdPromptMacroInsert;
  469.     {-Prompt for and insert a macro once}
  470.   var
  471.     MacNum : Integer;
  472.  
  473.   begin                      {EdPromptMacroInsert}
  474.     EdGetMacroNumber(304, MacNum);
  475.     if Abortcmd then
  476.       Exit;
  477.     EdInsertMacro(MacNum, 1);
  478.     ExitMenu := True;
  479.   end;                       {EdPromptMacroInsert}
  480.  
  481.   procedure EdEditKeyWindow(msg : VarString;
  482.                             Xmin, Ymin, Xmax, Ymax, Maxlen : Integer;
  483.                             var Keys : MacroString);
  484.     {-Set up a window, and edit the keystring}
  485.   var
  486.     W : WindowRec;
  487.     AsciiLength, ExtendedLength : LengthArray;
  488.     SaveCursorState : Boolean;
  489.  
  490.     procedure EdEditKeys(Xmin, Ymin, Xmax, Ymax, Maxlen : Integer; var Keys : MacroString);
  491.       {-edit a key sequence, default keys as input, keys also return result}
  492.     const
  493.       ScrollMask = $10;      {Mask for keyboard status flag, to separate scroll lock bit}
  494.     var
  495.       Ch : Char;
  496.       KbFlag : Byte absolute $0040 : $0017;
  497.       Mptr, ScrollLock, LastScroll : Byte;
  498.       Quitting, Inserting : Boolean;
  499.       CurRow, CurCol : Integer;
  500.       BlankLine : VarString;
  501.       RowPos, Colpos : array[1..255] of Integer;
  502.       KeyBuf : MacroString;
  503.  
  504.       procedure EdClrEol(Col, Row, Attr : Integer);
  505.         {-Clear to end of line}
  506.  
  507.       begin                  {EdClrEol}
  508.         BlankLine[0] := Chr(Xmax-Col);
  509.         EdFastWrite(BlankLine, Row, Col, Attr);
  510.       end;                   {EdClrEol}
  511.  
  512.       procedure EdClrEos(Col, Row, Attr : Integer);
  513.         {-Clear to end of window}
  514.       var
  515.         R : Integer;
  516.  
  517.       begin                  {EdClrEos}
  518.         EdClrEol(Col, Row, Attr);
  519.         for R := Succ(Row) to Ymax do
  520.           EdClrEol(Xmin, R, Attr);
  521.       end;                   {EdClrEos}
  522.  
  523.       procedure EdUpdateHelpLine;
  524.         {-Show the literal/command state}
  525.       const
  526.         StCom : string[9] = ' Command ';
  527.         StLit : string[9] = ' Literal ';
  528.       var
  529.         S : VarString;
  530.  
  531.       begin                  {EdUpdateHelpLine}
  532.         if ScrollLock <> 0 then
  533.           S := StLit
  534.         else
  535.           S := StCom;
  536.         EdFastWrite(S, Succ(Ymax), Xmax-12, ScreenAttr[MfColor]);
  537.       end;                   {EdUpdateHelpLine}
  538.  
  539.       procedure EdDisplayKeys(Row, Col, Mptr : Integer; Keys : MacroString);
  540.         {-Write the macro to the screen}
  541.       var
  542.         Attr : Byte;
  543.         CharStr : VarString;
  544.         Special : Boolean;
  545.  
  546.       begin                  {EdDisplayKeys}
  547.         if Row > Ymax then
  548.           Exit;
  549.         while Mptr <= Length(Keys) do begin
  550.           {Get the text representation of the next character group}
  551.           CharStr := EdTextRepresentation(Keys, Mptr, Special);
  552.           {Check for wrap}
  553.           if (Col+Length(CharStr) >= Xmax) then begin
  554.             {String starts next row}
  555.             EdClrEol(Col, Row, ScreenAttr[MnColor]);
  556.             Inc(Row);
  557.             if Row > Ymax then
  558.               Exit
  559.             else
  560.               Col := Xmin;
  561.           end;
  562.           if Special then
  563.             Attr := ScreenAttr[MhColor]
  564.           else
  565.             Attr := ScreenAttr[MnColor];
  566.           EdFastWrite(CharStr, Row, Col, Attr);
  567.  
  568.           Col := Col+Length(CharStr);
  569.         end;
  570.  
  571.         EdClrEos(Col, Row, ScreenAttr[MnColor]);
  572.       end;                   {EdDisplayKeys}
  573.  
  574.       procedure EdComputeScreenPos(Mptr : Byte; Keys : MacroString);
  575.         {-Compute the rowpos and colpos arrays, from mptr to length(keys)}
  576.       var
  577.         CharLen : Byte;
  578.         Row, Col : Integer;
  579.  
  580.         function EdMacLength(Keys : MacroString; var Kptr : Byte) : Byte;
  581.           {-Return display length of next character group}
  582.         var
  583.           Ch : Char;
  584.  
  585.         begin                {EdMacLength}
  586.           if Kptr <= Length(Keys) then begin
  587.             Ch := Keys[Kptr];
  588.             if UseExtendedSequence and (Ch = Null) and (Kptr < Length(Keys)) then begin
  589.               Inc(Kptr);
  590.               EdMacLength := ExtendedLength[Ord(Keys[Kptr])];
  591.             end else
  592.               EdMacLength := AsciiLength[Ord(Ch)];
  593.             Inc(Kptr);
  594.           end else
  595.             EdMacLength := 0;
  596.         end;                 {EdMacLength}
  597.  
  598.       begin                  {EdComputeScreenPos}
  599.         Row := RowPos[Mptr];
  600.         Col := Colpos[Mptr];
  601.         while Mptr <= Length(Keys) do begin
  602.           {Get the length of the next character group}
  603.           CharLen := EdMacLength(Keys, Mptr);
  604.           {Check for wrap}
  605.           if (Col+CharLen >= Xmax) then begin
  606.             {String starts next row}
  607.             if Row < Ymax then begin
  608.               Inc(Row);
  609.               Col := Xmin+CharLen;
  610.             end;
  611.           end else
  612.             Col := Col+CharLen;
  613.           {Store the positions}
  614.           RowPos[Mptr] := Row;
  615.           Colpos[Mptr] := Col;
  616.         end;
  617.       end;                   {EdComputeScreenPos}
  618.  
  619.       procedure EdIncKeyPos(var Mptr : Byte);
  620.         {-Move the current position one composite key to the right}
  621.  
  622.       begin                  {EdIncKeyPos}
  623.         if Mptr <= Length(Keys) then begin
  624.           if UseExtendedSequence and (Keys[Mptr] = Null) then
  625.             Inc(Mptr);
  626.           Inc(Mptr);
  627.         end;
  628.       end;                   {EdIncKeyPos}
  629.  
  630.       procedure EdDecKeyPos(var Mptr : Byte);
  631.         {-move the current position one composite key to left}
  632.  
  633.       begin                  {EdDecKeyPos}
  634.         if Mptr > 1 then begin
  635.           Dec(Mptr);
  636.           if UseExtendedSequence and (Keys[Pred(Mptr)] = Null) then
  637.             Dec(Mptr);
  638.         end;
  639.       end;                   {EdDecKeyPos}
  640.  
  641.       procedure EdInsertKey(CharStr : MacroString; var Mptr : Byte);
  642.         {-Insert a byte sequence into the current macro}
  643.  
  644.       begin                  {EdInsertKey}
  645.  
  646.         {Special case to handle nulls}
  647.         if not(UseExtendedSequence) and (CharStr[1] = Null) then
  648.           if (Length(CharStr) > 1) and (CharStr[2] = #3) then
  649.             {<Ctrl@> combination pressed, convert to true null}
  650.             CharStr := Null
  651.           else
  652.             {Some other combination pressed, ignore it}
  653.             Exit;
  654.  
  655.         if Length(Keys)+Length(CharStr) < Maxlen then begin
  656.           if not(Inserting) then begin
  657.             if UseExtendedSequence and (Keys[Mptr] = Null) then
  658.               {Delete the leader of an extended character}
  659.               Delete(Keys, Mptr, 1);
  660.             Delete(Keys, Mptr, 1);
  661.           end;
  662.           Insert(CharStr, Keys, Mptr);
  663.           EdDisplayKeys(RowPos[Mptr], Colpos[Mptr], Mptr, Keys);
  664.           EdComputeScreenPos(Mptr, Keys);
  665.           Mptr := Mptr+Length(CharStr);
  666.         end;
  667.       end;                   {EdInsertKey}
  668.  
  669.       procedure EdDeleteKey(Mptr : Byte);
  670.         {-Delete the keystroke at mptr}
  671.  
  672.       begin                  {EdDeleteKey}
  673.         if UseExtendedSequence and (Keys[Mptr] = Null) then
  674.           {Delete the leader of an extended character}
  675.           Delete(Keys, Mptr, 1);
  676.         Delete(Keys, Mptr, 1);
  677.         EdComputeScreenPos(Mptr, Keys);
  678.         EdDisplayKeys(RowPos[Mptr], Colpos[Mptr], Mptr, Keys);
  679.       end;                   {EdDeleteKey}
  680.  
  681.     begin                    {EdEditKeys}
  682.  
  683.       {Buffer the keys in case change is not to be stored}
  684.       KeyBuf := Keys;
  685.  
  686.       Quitting := False;
  687.       Inserting := True;
  688.       RowPos[1] := Ymin;
  689.       Colpos[1] := Xmin;
  690.       LastScroll := $FF;
  691.       EdEraseMenuHelp;
  692.       EdWritePromptLine(EdGetMessage(405));
  693.       Mptr := 1;
  694.       EdComputeScreenPos(Mptr, Keys);
  695.       FillChar(BlankLine[1], PhyScrCols, Blank);
  696.       EdDisplayKeys(RowPos[Mptr], Colpos[Mptr], Mptr, Keys);
  697.       EdSetInsertMode(Inserting);
  698.  
  699.       repeat
  700.  
  701.         GoToXY(Colpos[Mptr], RowPos[Mptr]);
  702.  
  703.         repeat
  704.           {Watch the scroll state while waiting for a keystroke}
  705.           ScrollLock := KbFlag and ScrollMask;
  706.           if ScrollLock <> LastScroll then begin
  707.             EdUpdateHelpLine;
  708.             LastScroll := ScrollLock;
  709.           end;
  710.           if Printing then
  711.             EdPrintNext(PrintChars);
  712.         until EdKeyPressed;
  713.  
  714.         Ch := EdGetInput;
  715.  
  716.         if (Ch = Null) then begin
  717.  
  718.           {Get extended scan code}
  719.           Ch := EdGetAnyChar;
  720.  
  721.           if ScrollLock <> 0 then
  722.  
  723.             {Literal mode, insert the key}
  724.             EdInsertKey(Null+Ch, Mptr)
  725.  
  726.           else
  727.             case Ch of
  728.  
  729.               #75 :          {Left arrow}
  730.                 EdDecKeyPos(Mptr);
  731.  
  732.               #77 :          {Right arrow}
  733.                 EdIncKeyPos(Mptr);
  734.  
  735.               #72 :          {Up arrow}
  736.                 begin
  737.                   CurRow := RowPos[Mptr];
  738.                   CurCol := Colpos[Mptr];
  739.                   repeat
  740.                     EdDecKeyPos(Mptr);
  741.                   until (Mptr = 1) or ((RowPos[Mptr] < CurRow) and (Colpos[Mptr] <= CurCol));
  742.                 end;
  743.  
  744.               #80 :          {Down arrow}
  745.                 begin
  746.                   CurRow := RowPos[Mptr];
  747.                   CurCol := Colpos[Mptr];
  748.                   repeat
  749.                     EdIncKeyPos(Mptr);
  750.                   until (Mptr > Length(Keys)) or ((RowPos[Mptr] > CurRow) and (Colpos[Mptr] >= CurCol));
  751.                 end;
  752.  
  753.               #82 :          {Ins}
  754.                 begin
  755.                   Inserting := not(Inserting);
  756.                   EdSetInsertMode(Inserting);
  757.                 end;
  758.  
  759.               #83 :          {Del}
  760.                 if Mptr <= Length(Keys) then
  761.                   EdDeleteKey(Mptr);
  762.  
  763.             else
  764.               {The key is to be part of the macro}
  765.               EdInsertKey(Null+Ch, Mptr);
  766.             end;
  767.         end else begin
  768.           {Not an extended scan code}
  769.  
  770.           if ScrollLock <> 0 then
  771.             EdInsertKey(Ch, Mptr)
  772.           else
  773.             case Ch of
  774.  
  775.               ^H :           {Backspace}
  776.                 if Mptr > 1 then begin
  777.                   EdDecKeyPos(Mptr);
  778.                   EdDeleteKey(Mptr);
  779.                 end;
  780.  
  781.               #27 :          {Escape}
  782.                 begin
  783.                   {Restore keys to original}
  784.                   Keys := KeyBuf;
  785.                   Quitting := True;
  786.                 end;
  787.  
  788.               ^M :           {Enter}
  789.                 Quitting := True;
  790.  
  791.               #127 :         {CtrlBkSp}
  792.                 begin
  793.                   EdClearString(Keys);
  794.                   Mptr := 1;
  795.                   EdDisplayKeys(RowPos[Mptr], Colpos[Mptr], Mptr, Keys);
  796.                 end;
  797.  
  798.             else
  799.               EdInsertKey(Ch, Mptr);
  800.             end;
  801.         end;
  802.  
  803.       until Quitting;
  804.  
  805.     end;                     {EdEditKeys}
  806.  
  807.   begin                      {EdEditKeyWindow}
  808.     {Turn off abort checking}
  809.     AbortEnable := False;
  810.  
  811.     {Set up an editing window}
  812.     EdSaveTextWindow(Border, msg, Xmin, Ymin, Xmax, Ymax, W);
  813.  
  814.     {Compute length of character display strings}
  815.     EdSetupKeyLength(AsciiLength, ExtendedLength);
  816.  
  817.     {Turn on hardware cursor and enable full screen addressing}
  818.     WindMin := 0;
  819.     WindMax := swap(pred(PhyScrRows)) or pred(PhyScrCols);
  820.     SaveCursorState := SolidCursor;
  821.     SolidCursor := False;
  822.  
  823.     {Edit the string}
  824.     with W do
  825.       EdEditKeys(XPosn+2, Succ(YPosn), Pred(XPosn+XSize), YPosn+YSize-2, Maxlen, Keys);
  826.  
  827.     {Restore screen}
  828.     EdRestoreTextWindow(W);
  829.     EdZapPromptLine;
  830.     SolidCursor := SaveCursorState;
  831.     EdSetCursorOff;
  832.  
  833.   end;                       {EdEditKeyWindow}
  834.  
  835.   procedure EdEditMacro;
  836.     {-Edit a macro}
  837.   var
  838.     N : Integer;
  839.     Mname : String255;
  840.  
  841.   begin                      {EdEditMacro}
  842.  
  843.     {Prompt for which macro to edit}
  844.     EdGetMacroNumber(404, N);
  845.     if Abortcmd or not(N in [0..MaxMacro]) then
  846.       Exit;
  847.  
  848.     {Edit the macro name}
  849.     EdGetMacroName(N);
  850.     if Abortcmd then
  851.       Exit;
  852.     if EdPtrIsNil(Macronames[N]) then
  853.       Mname := EdGetMessage(384)
  854.     else
  855.       Mname := Blank+Macronames[N]^+Blank;
  856.  
  857.     {Edit the keystrokes}
  858.     EdEditKeyWindow(Mname, 5, 15, 75, 25, MaxMacroLength, Macrokeys[N]);
  859.  
  860.   end;                       {EdEditMacro}
  861.  
  862. begin
  863.   {Initialize macros to nil}
  864.   FillChar(Macrokeys, SizeOf(Macrokeys), 0);
  865.   FillChar(Macronames, SizeOf(Macronames), 0);
  866.   Macronum := 0;
  867. end.
  868.