home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / PULLDOWN.ZIP / PULL11.INC < prev    next >
Encoding:
Text File  |  1987-02-27  |  27.0 KB  |  903 lines

  1. { Pull11.inc - Turbo Pascal full featured pull-down menus.  ver 1.1, 02-27-87 }
  2. { (c) 1987 James H. LeMay, Arthur J. Hill                                     }
  3. type
  4.   SelectModeType = (Choice, NoChoice, ToDataWndw, ToSubMenu);
  5.   MenuModeType   = (ExecChoice, SingleChoice, MultipleChoice);
  6.   TypeOfDataType = (Bytes, Integers, Reals, Chars, Strings);
  7.   Toggle         = (Off, On, No, Yes);
  8.   MaxString      = string[MaxStringLength];
  9.   ErrMsgString   = string[MaxErrStrLength];
  10.   { Careful! -- if you change any record, ClearPullStats must also be changed.}
  11.   MenuRec = record
  12.               Title:        string[MaxCharsPerLine];
  13.               CmdLtrs:      string[MaxMenuLines];
  14.               Line:         array[1..MaxMenuLines] of string[MaxCharsPerLine];
  15.               Selected:     array[1..MaxMenuLines] of boolean;
  16.               SelectMode:   array[1..MaxMenuLines] of SelectModeType;
  17.               LinkNum:      array[1..MaxMenuLines] of byte;
  18.               LinkDir:      DirType;
  19.               MenuMode:     MenuModeType;
  20.               MenuLines:    byte;
  21.               NameCol:      byte;
  22.               Row, Col, Rows, Cols, DefaultLine, HiLited, SingleSel: byte;
  23.               Wattr, HiAttr, Battr:     byte;
  24.               Border:                   Borders;
  25.               BackToDefault, Changed:   boolean;
  26.               MsgLineNum, HelpWndwNum:  byte
  27.             end;
  28.   DataWndwRec = record
  29.               Line: array[1..2] of string[MaxCharsPerLine];
  30.               TypeOfData:               TypeOfDataType;
  31.               Row, Col, Rows, Cols:     byte;
  32.               RowAlt, ColAlt:           byte;
  33.               FirstCol, Field:          byte;
  34.               Decimals:                 integer;
  35.               Wattr, HiAttr, Battr:     byte;
  36.               Border:                   Borders;
  37.               MsgLineNum, HelpWndwNum:  byte;
  38.               ErrorMsgNum:              integer;
  39.             end;
  40.   HelpWndwRec = record
  41.               FirstLine, LastLine:      byte;
  42.               LinesToShow:              byte;
  43.               Row, Col, Rows, Cols:     byte;
  44.               Wattr, Battr:             byte;
  45.               Border:                   Borders;
  46.               Zoom:                     boolean;
  47.               Shadow:                   DirType;
  48.               MsgLineNum:               byte
  49.             end;
  50.   DataPadRec = record
  51.               Store:      boolean;
  52.               case TypeOfData: TypeOfDataType of
  53.                 Bytes:    (B: byte);
  54.                 Integers: (I: integer);
  55.                 Reals:    (R: real);
  56.                 Chars:    (C: char);
  57.                 Strings:  (S: MaxString);
  58.             end;
  59.   MainMenuRecs  = array[1..NumOfMainMenus] of MenuRec;
  60.   SubMenuRecs   = array[1..NumOfSubMenus]  of MenuRec;
  61.   DataWndwRecs  = array[1..NumOfDataWndws] of DataWndwRec;
  62.   HelpWndwRecs  = array[1..NumOfHelpWndws] of HelpWndwRec;
  63.  
  64. const
  65.   HideCursor: integer = $2020;
  66.  
  67. var
  68.   MainMenu: MainMenuRecs;
  69.   SubMenu:  SubMenuRecs;
  70.   DataWndw: DataWndwRecs;
  71.   HelpWndw: HelpWndwRecs;
  72.   HelpLine: array[1..TotalHelpLines] of string[HelpCharsPerLine];
  73.   MsgLine:  array[1..NumOfMsgLines] of MaxString;
  74.   ErrMsgLine: array[1..NumOfErrMsgLines] of ErrMsgString;
  75.  
  76.   TopMenuRow,    MainMenuRow,
  77.   TopMenuAttr,   TopHilitAttr,  MsgLineAttr,
  78.   MainMenuWattr, MainMenuBattr, MainHilitAttr,
  79.   SubMenuWattr,  SubMenuBattr,  SubHilitAttr,
  80.   DataWndwWattr, DataWndwBattr,
  81.   HelpWndwWattr, HelpWndwBattr: byte;
  82.   MainMenuBrdr,  SubMenuBrdr, DataWndwBrdr, HelpWndwBrdr: Borders;
  83.   HelpShadow:    DirType;
  84.   HelpZoom:      boolean;
  85.   TopCmdLtrs:    string[NumOfMainMenus];
  86.   CmdSeq,LastCmdSeq: string[MaxWndw];
  87.   RowsBelowHelp,RowsBelowMsg: byte;
  88.  
  89.   CRTcols, CRTrows: integer;
  90.   CRTcolumns:  integer absolute $0040:$004A;
  91.   KeyStat:     byte    absolute $0040:$0017;
  92.   AutoNumLock: boolean;
  93.   LastKeyStat: byte;
  94.  
  95.   OldCursor, DOScursor, MainScrCursor,
  96.   LastMainPulled, MPulled, SPulled,i,j: integer;
  97.   TopMenuStr:        MaxString;
  98.   DataPad:           DataPadRec;
  99.   ExtKey, Quit:      boolean;
  100.   LocationWarning:   boolean;
  101.   Pull, Pop, ToTop:  boolean;
  102.   Ch:                char;
  103.  
  104. procedure DataTransfer (VAR DataPad: DataPadRec; HiLt: byte;
  105.                         VAR ErrMsg: integer); forward;
  106. procedure Process (MPulled,SPulled,HiLt: byte); forward;
  107. procedure GetUserPullStats; forward;
  108. procedure GetOverrideStats; forward;
  109.  
  110. procedure ReadKB (VAR ExtKey: boolean; VAR Ch: char);
  111. begin
  112.   Read (Kbd,Ch);                      { Read keyboard input.      }
  113.   if KeyPressed and (Ch=^[) then      { If first Char was ESC ... }
  114.     begin
  115.       Read (Kbd,Ch);                  { ... read second char.     }
  116.       ExtKey := true
  117.     end
  118.   else ExtKey:=false;
  119. end;
  120.  
  121. procedure NumLockOn;
  122. begin
  123.   LastKeyStat := KeyStat;
  124.   KeyStat := LastKeyStat or $20;
  125.   QwriteC (CRTrows,1,CRTcols,-1,'NUMLOCK')
  126. end;
  127.  
  128. procedure NumLockOff;
  129. begin
  130.   KeyStat := (KeyStat and $DF) or (LastKeyStat and $20)
  131. end;
  132.  
  133. procedure ShowMsg (MsgNum: byte);
  134. begin
  135.     QwriteV (CRTrows-RowsBelowMsg,1,MsgLineAttr,MsgLine[MsgNum])
  136. end;
  137.  
  138. procedure ShowMenu (VAR Menu: MenuRec);
  139. var C:      byte;
  140.     Symbol: string[1];
  141. begin
  142.   With Menu do
  143.   begin
  144.     MakeWindow (Row,Col,Rows,Cols,Wattr,Battr,Border);
  145.     for i:=1 to Rows do
  146.     begin
  147.       if Selected[i] then Qwrite (Row+i,Col+2,-1,#16);
  148.       QwriteV (Row+i,Col+3,-1,Line[i]);
  149.       if SelectMode[i] in [ToSubMenu,ToDataWndw] then
  150.       begin
  151.         case LinkDir of
  152.           Left:  C:=Col+1;
  153.           Right: C:=Col+Cols-2;
  154.         end;
  155.         case SelectMode[i] of
  156.           ToSubMenu:  Symbol:=#240;
  157.           ToDataWndw: Symbol:=#250;
  158.         end;
  159.         QwriteV (Row+i,C,-1,Symbol)
  160.       end
  161.     end;
  162.     if BackToDefault then HiLited:=DefaultLine;
  163.     Qattr (Row+HiLited,Col+1,1,Cols-2,HiAttr);
  164.   end
  165. end;
  166.  
  167. procedure RollHiLite (VAR Menu: MenuRec; Dir: DirType);
  168. begin
  169.   With Menu do
  170.   begin
  171.     Qattr (Row+HiLited,Col+1,1,Cols-2,Wattr);
  172.     repeat
  173.       case Dir of
  174.         Up:     if HiLited=1 then HiLited:=MenuLines else HiLited:=HiLited-1;
  175.         Down:   if HiLited=MenuLines then HiLited:=1 else HiLited:=HiLited+1;
  176.         Top:    begin
  177.                   HiLited:=1;
  178.                   Dir:=Down
  179.                 end;
  180.         Bottom: begin
  181.                   HiLited:=MenuLines;
  182.                   Dir:=Up
  183.                 end;
  184.       end;  { case }
  185.     until SelectMode[HiLited]<>NoChoice;
  186.     Qattr (Row+HiLited,Col+1,1,Cols-2,HiAttr)
  187.   end
  188. end;
  189.  
  190. procedure ShowTopHiLited;
  191. begin
  192.   with MainMenu[MPulled] do
  193.     Qattr (TopMenuRow,NameCol,1,length(Title)+2,TopHilitAttr);
  194. end;
  195.  
  196. procedure ClearTopHiLited;
  197. begin
  198.   Qattr (TopMenuRow,1,1,CRTcols,TopMenuAttr);
  199. end;
  200.  
  201. procedure RollMenu (Dir: DirType);
  202. begin
  203.   RemoveWindow;
  204.   ClearTopHiLited;
  205.   case Dir of
  206.     Left:     if MPulled=1 then MPulled:=NumOfMainMenus else MPulled:=MPulled-1;
  207.     Right:    if MPulled=NumOfMainMenus then MPulled:=1 else MPulled:=MPulled+1;
  208.     FarLeft:  MPulled:=1;
  209.     FarRight: MPulled:=NumOfMainMenus;
  210.   end;
  211.   with MainMenu[MPulled] do
  212.   begin
  213.     ShowTopHiLited;
  214.     ShowMenu (MainMenu[MPulled]);
  215.     ShowMsg (MsgLineNum)
  216.   end;
  217.   CmdSeq:=TopCmdLtrs[MPulled]
  218. end;
  219.  
  220. procedure DoChoice (VAR Menu: MenuRec; VAR Sel: boolean);
  221. type Str1 = string[1];
  222. {}procedure ShowFlag (Flag: Str1);
  223.   begin
  224.     with Menu do
  225.       QwriteV (Row+HiLited,Col+2,-1,Flag)
  226. {}end;
  227. begin
  228.   with Menu do
  229.   case MenuMode of
  230.     ExecChoice:     Process (MPulled,SPulled,HiLited);
  231.     SingleChoice:   if Sel<>true then
  232.                     begin
  233.                       Selected[SingleSel] := false;
  234.                       Sel := true;
  235.                       Qwrite (Row+SingleSel,Col+2,-1,' ');
  236.                       ShowFlag (^P);
  237.                       SingleSel := HiLited;
  238.                       Changed := true
  239.                     end;
  240.     MultipleChoice: begin
  241.                       Changed := true;
  242.                       case Sel of
  243.                         true:  begin
  244.                                  Sel:=false;
  245.                                  ShowFlag (' ')
  246.                                end;
  247.                         false: begin
  248.                                  Sel:=true;
  249.                                  ShowFlag (^P)
  250.                                end
  251.                       end
  252.                     end;
  253.   end  { case }
  254. end;
  255.  
  256. procedure PullHelpWndw (WndwNum,MsgLNum: byte; Title: MaxString);
  257. begin
  258.   CursorChange (HideCursor,OldCursor);
  259.   with HelpWndw[WndwNum] do
  260.   begin
  261.     ZoomEffect:=HelpZoom;
  262.     ShadowEffect:=HelpShadow;
  263.     ShowMsg (MsgLineNum);
  264.     MakeWindow (Row,Col,Rows,Cols,Wattr,Battr,Border);
  265.     TitleWindow (Center,' Help for "'+Title+'" ');
  266.     for i:=FirstLine to FirstLine+LinesToShow-1 do
  267.       QwriteV (Row+1+i-FirstLine,Col+2,-1,HelpLine[i]);
  268.   end;
  269.   repeat
  270.     ReadKB (ExtKey, Ch);
  271.     if ExtKey and (Ch=#59) then Ch:=^[; { F1  key }
  272.   until Ch=^[;                          { ESC key }
  273.   Ch:=' ';
  274.   RemoveWindow;
  275.   ShowMsg (MsgLNum);
  276.   CursorChange (OldCursor,i);
  277.   ZoomEffect:=false;
  278.   ShadowEffect:=NoDir
  279. end;
  280.  
  281. procedure TurnArrows (Switch: Toggle; VAR Menu: MenuRec);
  282. var Arrow: string[1];
  283.     R,C: byte;
  284. begin
  285.   with Menu do
  286.   begin
  287.     R:=Row+HiLited;
  288.     case LinkDir of
  289.       Left:  begin Arrow:=^Q; C:=1; end;
  290.       Right: begin Arrow:=^P; C:=0; end;
  291.     end;
  292.     if Switch=Off then Arrow:=' ';
  293.     QwriteV (R,Col+1+C     ,-1,Arrow);
  294.     QwriteV (R,Col+Cols-3+C,-1,Arrow);
  295.     if not Pop and (Switch=Off) then ShowMsg (MsgLineNum)
  296.   end
  297. end;
  298.  
  299. procedure ShowDataWndw (VAR Menu: MenuRec; VAR DWndw: DataWndwRec);
  300. var DataPadStr:                   MaxString;
  301.     FieldCol,PadStrCol,CursorCol: byte;
  302. {}procedure FindRowCol;
  303.   begin
  304.     with DWndw do
  305.       if RowAlt+ColAlt=0 then
  306.         begin
  307.           Row:=Menu.Row+Menu.HiLited;
  308.           if (Row+Rows)>CRTrows-2 then Row:=CRTrows-Rows-1;
  309.           case Menu.LinkDir of
  310.             Right: Col:=Menu.Col+(Menu.Cols-2);
  311.             Left:  Col:=Menu.Col-(Cols-2)
  312.           end
  313.         end
  314.       else
  315.         begin
  316.           Row:=RowAlt;
  317.           Col:=ColAlt
  318.         end;
  319. {}end;
  320. {}procedure ConvertDataToStr;
  321.   var ErrMsg: integer;
  322.   begin
  323.     with DWndw do
  324.     begin
  325.       DataPad.TypeOfData := TypeOfData;
  326.       DataPad.Store := false;
  327.       DataTransfer (DataPad,Menu.HiLited,ErrMsg);
  328.       with DataPad do
  329.         case TypeOfData of
  330.           Bytes:     Str(B:Field,DataPadStr);
  331.           Integers:  Str(I:Field,DataPadStr);
  332.           Reals:     if Decimals<0 then Str(R:Field,DataPadStr)
  333.                      else
  334.                        begin
  335.                          Str(R:Field:Decimals,DataPadStr);
  336.                          if length(DataPadStr)>Field then
  337.                            Str(R:Field,DataPadStr)
  338.                        end;
  339.           Chars:     DataPadStr:='"'+C+'"';
  340.           Strings:   DataPadStr:='"'+S+'"';
  341.         end
  342.     end
  343. {}end;
  344. begin
  345.   with DWndw do
  346.   begin
  347.     FindRowCol;
  348.     MakeWindow (Row,Col,Rows,Cols,Wattr,Battr,Border);
  349.     for i:=1 to 2 do
  350.       QwriteV (Row+i,Col+2,-1,Line[i]);
  351.     ConvertDataToStr;
  352.     ShowMsg (MsgLineNum);
  353.     FieldCol:=Col+FirstCol;
  354.     case TypeOfData of
  355.       Bytes..Reals:  begin
  356.                        PadStrCol:=FieldCol;
  357.                        CursorCol:=FieldCol+Field-1;
  358.                        if AutoNumLock then NumLockOn
  359.                      end;
  360.       Chars,Strings: begin
  361.                        PadStrCol:=FieldCol-1;
  362.                        CursorCol:=FieldCol
  363.                      end;
  364.     end;
  365.     QwriteV (Row+1,PadStrCol     ,-1,DataPadStr);
  366.     Qwrite  (Row+2,FieldCol-1    ,-1,'»');
  367.     Qwrite  (Row+2,FieldCol+Field,-1,'«');
  368.     GotoRC  (Row+2,CursorCol);
  369.     CursorChange (DOScursor,i);
  370.   end
  371. end;
  372.  
  373. procedure CheckForPull;
  374. begin
  375.   if Pull then
  376.   begin
  377.     if LastCmdSeq='' then Pull:=false
  378.     else
  379.       begin
  380.         Ch:=LastCmdSeq[1];
  381.         Delete (LastCmdSeq,1,1)
  382.       end;
  383.     ExtKey:=false
  384.   end
  385. end;
  386.  
  387. procedure ShowErrorMsg (ErrMsgNum: integer; MsgLnNum: byte);
  388. var Row,L: byte;
  389. begin
  390.   Row:=CRTrows-RowsBelowMsg;
  391.   QwriteV (Row,1,MsgLineAttr,ErrMsgLine[ErrMsgNum]);
  392.   L:=length(ErrMsgLine[ErrMsgNum]);
  393.   Qfill   (Row,L+1,1,CRTcols-L,-1,' ');
  394.   repeat
  395.     sound (100);
  396.     delay (30);
  397.     nosound;
  398.     ReadKB (ExtKey,Ch);
  399.   until Ch=^[;
  400.   Ch:=' ';
  401.   ShowMsg (MsgLnNum)
  402. end;
  403.  
  404. procedure PullDataWndw (VAR Menu: MenuRec; WndwNum: byte);
  405. {}procedure AcceptDataEntry;
  406.   var ValidChSet:            set of char;
  407.       DataEntryStr,VideoStr: MaxString;
  408.       C:                     byte;
  409.       Space:                 string[1];
  410.       Null:                  boolean;
  411. {--}procedure AppendStr;
  412.     var L: byte;
  413.     begin
  414.       L:=length(DataEntryStr);
  415.       Space:='';
  416.       Null:=false;
  417.       case Ch of
  418.         ^H:  begin
  419.                if L>0 then
  420.                begin
  421.                  DataEntryStr[0]:=pred(DataEntryStr[0]);
  422.                  Space:=' '
  423.                end
  424.              end;
  425.       else if L<DataWndw[WndwNum].Field then
  426.              DataEntryStr:=DataEntryStr+Ch;
  427.       end  { case }
  428. {--}end;
  429. {--}procedure StoreData;
  430.     var Errors: integer;
  431.     begin
  432.       with DataPad do
  433.       begin
  434.         Errors:=0;
  435.         case TypeOfData of
  436.           Bytes..Reals:
  437.             begin
  438.               case TypeOfData of
  439.                 Bytes:    begin
  440.                             val(DataEntryStr,I,Errors);
  441.                             if (Errors=0)and(I>255) then Errors:=1;
  442.                           end;
  443.                 Integers: val(DataEntryStr,I,Errors);
  444.                 Reals:    val(DataEntryStr,R,Errors);
  445.               end;  { case }
  446.               if Errors<>0 then
  447.                 with DataWndw[WndwNum] do
  448.                   ShowErrorMsg (ErrorMsgNum,MsgLineNum);
  449.             end;
  450.           Chars:   if Null then
  451.                         C:=^@
  452.                    else C:=DataEntryStr[1];
  453.           Strings: S:=DataEntryStr;
  454.         end;  { case }
  455.         if Errors=0 then
  456.         begin
  457.           Store:=true;
  458.           DataTransfer (DataPad,Menu.HiLited,Errors);
  459.           if Errors<>0 then
  460.                ShowErrorMsg (Errors,DataWndw[WndwNum].MsgLineNum)
  461.           else Menu.Changed := true
  462.         end
  463.       end   { with }
  464. {--}end;  { procedure }
  465. {}begin
  466.     with DataWndw[WndwNum] do
  467.     begin
  468.       DataEntryStr:='';
  469.       Null:=false;
  470.       case TypeOfData of
  471.         Bytes:           ValidChSet:=['0'..'9',^H];
  472.         Integers:        ValidChSet:=['0'..'9','-','+',^H];
  473.         Reals:           ValidChSet:=['0'..'9','-','+','.','E','e',^H];
  474.         Chars, Strings:  ValidChSet:=[' '..'~',^H,#00]
  475.       end;
  476.       if not Pull then CmdSeq:=CmdSeq+Menu.CmdLtrs[Menu.HiLited];
  477.       CheckForPull;
  478.       repeat
  479.         ReadKB (ExtKey,Ch);
  480.         if ExtKey then
  481.           case Ch of
  482.             #59: PullHelpWndw (HelpWndwNum,MsgLineNum,
  483.                                 Menu.Line[Menu.HiLited]); { F1 }
  484.             #83: if #00 in ValidChSet then
  485.                  begin
  486.                    Qfill (Row+2,Col+FirstCol,1,Field,-1,' ');
  487.                    DataEntryStr:='';
  488.                    Null:=true;
  489.                    GotoRC (Row+2,Col+FirstCol)
  490.                  end;
  491.             #60: Pop:=true;  { F2 }
  492.           end      { end case }
  493.         else
  494.           if Ch in ValidChSet then
  495.             begin
  496.               case TypeOfData of
  497.                 Bytes..Reals:
  498.                   begin
  499.                     AppendStr;
  500.                     VideoStr:=Space+DataEntryStr;
  501.                     C:=Col+FirstCol+Field-length(VideoStr);
  502.                   end;  { Bytes..Reals }
  503.                 Chars,Strings:
  504.                   begin
  505.                     AppendStr;
  506.                     VideoStr:=DataEntryStr+Space;
  507.                     C:=Col+FirstCol;
  508.                     GotoRC (Row+2,Col+FirstCol+length(DataEntryStr))
  509.                   end;  { Chars,Strings }
  510.               end;  { case }
  511.               QwriteV (Row+2,C,-1,VideoStr)
  512.             end
  513.           else
  514.             if Ch='/' then ToTop:=true;
  515.         if (Ch=^M) and ((DataEntryStr<>'') or Null) then StoreData;
  516.       until (Ch in [^M,^[]) or Pop or ToTop;
  517.       if not Pop then CmdSeq[0]:=pred(CmdSeq[0]);
  518.       if (TypeOfData in [Bytes..Reals]) and AutoNumLock then NumLockOff
  519.     end  { with }
  520. {}end;
  521. begin
  522.   TurnArrows (On,Menu);
  523.   ShowDataWndw (Menu,DataWndw[WndwNum]);
  524.   AcceptDataEntry;
  525.   with Menu do
  526.     begin
  527.       if (MenuMode=ExecChoice)and(Ch=^M) then Process(MPulled,SPulled,HiLited);
  528.       Ch:=' '
  529.     end;
  530.   CursorChange (HideCursor,i);
  531.   RemoveWindow;
  532.   TurnArrows (Off,Menu)
  533. end;
  534.  
  535. procedure PullSubMenu (VAR Menu: MenuRec; MenuNum: byte); forward;
  536.  
  537. procedure CheckSelection (VAR Menu: MenuRec);
  538. var Position: byte;
  539. {}procedure MoveHiLite (Att: byte);
  540.   begin
  541.     with Menu do
  542.       Qattr (Row+HiLited,Col+1,1,Cols-2,Att)
  543. {}end;
  544. begin
  545.   with Menu do
  546.   begin
  547.     if Ch='/' then ToTop:=true;
  548.     Position := pos (upcase(Ch),CmdLtrs);
  549.     if Position<>0 then
  550.       begin
  551.         MoveHiLite (Wattr);
  552.         HiLited := Position;
  553.         MoveHiLite (HiAttr);
  554.         Ch:=^M
  555.       end;
  556.     if Ch=^M then
  557.       case SelectMode[HiLited] of
  558.         ToSubMenu:  PullSubMenu  (Menu,LinkNum[HiLited]);
  559.         ToDataWndw: PullDataWndw (Menu,LinkNum[HiLited]);
  560.         Choice:     DoChoice     (Menu,Selected[HiLited]);
  561.       end;
  562.   end  { with }
  563. end;
  564.  
  565. procedure PullSubMenu;
  566. begin
  567.   SPulled:=MenuNum;
  568.   with Menu do
  569.     if not Pull then CmdSeq:=CmdSeq+CmdLtrs[HiLited];
  570.   TurnArrows (On,Menu);
  571.   ShowMenu (SubMenu[MenuNum]);
  572.   with SubMenu[MenuNum] do
  573.   begin
  574.     CheckForPull;
  575.     if not Pull then ShowMsg (MsgLineNum);
  576.     repeat
  577.       if not Pull then ReadKB (ExtKey,Ch);
  578.       if ExtKey then
  579.         case Ch of
  580.           #72:     RollHiLite (SubMenu[MenuNum],Up    );  { Up   arrow }
  581.           #80:     RollHiLite (SubMenu[MenuNum],Down  );  { Down arrow }
  582.           #71,#73: RollHiLite (SubMenu[MenuNum],Top   );  { Home and PgUp }
  583.           #79,#81: RollHiLite (SubMenu[MenuNum],Bottom);  { End  and PgDn }
  584.           #59:     PullHelpWndw (HelpWndwNum,MsgLineNum,Title);  { F1 }
  585.           #60:     Pop:=true; { F2 }
  586.         end      { end case }
  587.       else  CheckSelection (SubMenu[MenuNum]);
  588.     until (Ch=^[) or Pop or ToTop;
  589.     Ch:=' ';
  590.     RemoveWindow;
  591.     if not Pop then CmdSeq[0]:=pred(CmdSeq[0]);
  592.     TurnArrows (Off,Menu)
  593.   end  { with }
  594. end;
  595.  
  596. procedure PullMainMenu;
  597. begin
  598.   SPulled:=0;
  599.   if not Pull then CmdSeq:=TopCmdLtrs[MPulled];
  600.   ShowTopHiLited;
  601.   ShowMenu (MainMenu[MPulled]);
  602.   CheckForPull;
  603.   if not Pull then ShowMsg (MainMenu[MPulled].MsgLineNum);
  604.   repeat
  605.     if not Pull then ReadKB (ExtKey,Ch);
  606.     with MainMenu[MPulled] do
  607.       if ExtKey then
  608.         case Ch of
  609.           #72:      RollHiLite (MainMenu[MPulled],Up      );  { Up   arrow }
  610.           #80:      RollHiLite (MainMenu[MPulled],Down    );  { Down arrow }
  611.           #73:      RollHiLite (MainMenu[MPulled],Top     );  { PgUp }
  612.           #81:      RollHiLite (MainMenu[MPulled],Bottom  );  { PgDn }
  613.           #75:      RollMenu   (                  Left    );  { Left  arrow }
  614.           #77:      RollMenu   (                  Right   );  { Right arrow }
  615.           #71,#115: RollMenu   (                  FarLeft );  { Home & ^Left }
  616.           #79,#116: RollMenu   (                  FarRight);  { End & ^Right }
  617.           #59:      PullHelpWndw (HelpWndwNum,MsgLineNum,Title);  { F1 }
  618.           #60:      Pop:=true; { F2 }
  619.         end      { end case }
  620.       else
  621.         begin
  622.           CheckSelection (MainMenu[MPulled]);
  623.           SPulled:=0
  624.         end
  625.   until (Ch=^[) or Pop or ToTop;
  626.   Ch := ' ';
  627.   RemoveWindow;
  628.   if not Pop then
  629.     begin
  630.       CmdSeq:='';
  631.       ShowMsg (2)
  632.     end
  633. end;
  634.  
  635. procedure PullTopMenu;
  636. var Position: byte;
  637. begin
  638.   LastMainPulled:=MPulled;
  639.   ShowTopHiLited;
  640.   CheckForPull;
  641.   if not Pull then ShowMsg (2);
  642.   repeat
  643.     if not Pull then ReadKB (ExtKey,Ch);
  644.     if ExtKey then
  645.       begin
  646.         case Ch of
  647.           #75:      if MPulled=1 then                { Left  arrow }
  648.                          MPulled:=NumOfMainMenus
  649.                     else MPulled:=MPulled-1;
  650.           #77:      if MPulled=NumOfMainMenus then   { Right arrow }
  651.                          MPulled:=1
  652.                     else MPulled:=MPulled+1;
  653.           #71,#115: MPulled:=1;                      { Home & ^Left }
  654.           #79,#116: MPulled:=NumOfMainMenus;         { End & ^Right }
  655.           #59:      PullHelpWndw (2,2,'Top Menu');   { F1 }
  656.           #60:      Pop:=true;                       { F2 }
  657.         end;
  658.         if MPulled<>LastMainPulled then
  659.           begin
  660.             LastMainPulled:=MPulled;
  661.             ClearTopHiLited;
  662.             ShowTopHiLited
  663.           end
  664.       end
  665.     else
  666.       begin
  667.         Position := pos (upcase(Ch),TopCmdLtrs);
  668.         if Position<>0 then
  669.           begin
  670.             MPulled := Position;
  671.             Ch:=^M
  672.           end;
  673.         if Ch=^M then
  674.           begin
  675.             if LastMainPulled<>MPulled then ClearTopHiLited;
  676.             ToTop:=false;
  677.             PullMainMenu;
  678.             LastMainPulled := MPulled
  679.           end
  680.       end;
  681.   until (Ch=^[) or Pop;
  682. end;
  683.  
  684. procedure GoToMenus;
  685. begin
  686.   Pop := false;
  687.   CursorChange (HideCursor,i);
  688.   case Ch of
  689.     ^[:  begin
  690.            LastCmdSeq:=CmdSeq;
  691.            Pull:=true
  692.          end;
  693.     '/': begin
  694.            CmdSeq:='';
  695.            Pull:=false
  696.          end;
  697.   end;
  698.   PullTopMenu;
  699.   ClearTopHiLited;
  700.   CursorChange (MainScrCursor,i)
  701. end;
  702.  
  703. procedure InitMenuSizeAndColor;
  704. var Lmax,L,L2: integer;
  705. {}procedure GetRowsAndCols (VAR Menu: MenuRec);
  706.   var CmdLtr: char;
  707.   begin
  708.     with Menu do
  709.     begin
  710.       Rows := MenuLines+2;
  711.       Lmax := 0;
  712.       CmdLtrs := '';
  713.       for j:=1 to MenuLines do
  714.       begin
  715.         L := length (Line[j]);
  716.         if L>Lmax then Lmax:=L;
  717.         if SelectMode[j]<>NoChoice then
  718.              CmdLtr:=upcase(Line[j][1])
  719.         else CmdLtr:=^@;
  720.         CmdLtrs := CmdLtrs + CmdLtr;
  721.       end;
  722.       Cols:= Lmax+6
  723.     end
  724. {}end;  { procedure }
  725. begin
  726.   for i:=1 to NumOfMainMenus do
  727.     with MainMenu[i] do
  728.     begin
  729.       GetRowsAndCols (MainMenu[i]);
  730.       HiAttr := MainHiLitAttr;
  731.       Wattr := MainMenuWattr;
  732.       Battr := MainMenuBattr;
  733.       Border := MainMenuBrdr;
  734.       HiLited := DefaultLine;
  735.       if MenuMode=SingleChoice then Selected[SingleSel]:=true
  736.     end;
  737.   for i:=1 to NumOfSubMenus do
  738.   begin
  739.     with SubMenu[i] do
  740.     begin
  741.       GetRowsAndCols (SubMenu[i]);
  742.       HiAttr := SubHiLitAttr;
  743.       Wattr := SubMenuWattr;
  744.       Battr := SubMenuBattr;
  745.       Border := SubMenuBrdr;
  746.       HiLited := DefaultLine;
  747.       if MenuMode=SingleChoice then Selected[SingleSel]:=true
  748.     end;
  749.   end;
  750.   for i:=1 to NumOfDataWndws do
  751.   begin
  752.     with DataWndw[i] do
  753.     begin
  754.       Rows := 4;
  755.       L  := length(Line[1]);
  756.       L2 := length(Line[2]);
  757.       if L>=L2 then Lmax:=L else Lmax:=L2;
  758.       Cols := Lmax+6+Field;
  759.       FirstCol := Lmax+4;
  760.       Wattr  := DataWndwWattr;
  761.       Battr  := DataWndwBattr;
  762.       Border := DataWndwBrdr
  763.     end;
  764.   end;
  765.   for i:=1 to NumOfHelpWndws do
  766.   begin
  767.     with HelpWndw[i] do
  768.     begin
  769.       Rows   := LastLine-FirstLine+3;
  770.       Cols   := HelpCharsPerLine+4;
  771.       Wattr  := HelpWndwWattr;
  772.       Battr  := HelpWndwBattr;
  773.       Border := HelpWndwBrdr;
  774.       Zoom   := HelpZoom;
  775.       Shadow := HelpShadow
  776.     end;
  777.   end
  778. end;
  779.  
  780. procedure LocateMainMenus;
  781. begin
  782.   fillchar (TopMenuStr,CRTcols+1,' ');
  783.   TopMenuStr:=' ≡';
  784.   TopCmdLtrs:='';
  785.   for i:=1 to NumOfMainMenus do
  786.   begin
  787.     with MainMenu[i] do
  788.     begin
  789.       Row := MainMenuRow;
  790.       Col := length(TopMenuStr)+1;
  791.       NameCol := Col+1;
  792.       TopMenuStr := TopMenuStr + '  ' + Title;
  793.       TopCmdLtrs := TopCmdLtrs + upcase(Title[1]);
  794.       if Cols+Col>CRTcols-1 then Col:=CRTcols-1-Cols;
  795.     end;
  796.   end;
  797.   TopMenuStr[0] := char(CRTcols)
  798. end;
  799.  
  800. procedure LocateSubMenus;   { and DataWndws }
  801. var RoomL,RoomR,RoomMax,TestWidth,QtyL,QtyR: byte;
  802. {}procedure FindLinkDir (VAR Menu: MenuRec);
  803.   begin
  804.     with Menu do
  805.     begin
  806.       RoomL := Col;
  807.       RoomR := CRTcols-(Col+Cols-1);
  808.       if RoomR>=RoomL then RoomMax:=RoomR else RoomMax:=RoomL;
  809.       QtyL:=0; QtyR:=0;
  810.       for j:=1 to MenuLines do
  811.       begin
  812.         if SelectMode[j] in [ToDataWndw,ToSubMenu] then
  813.         begin
  814.           case SelectMode[j] of
  815.             ToSubMenu:  TestWidth:=SubMenu[LinkNum[j]].Cols;
  816.             ToDataWndw: TestWidth:=DataWndw[LinkNum[j]].Cols;
  817.           end;
  818.           if TestWidth<=RoomMax then
  819.             begin
  820.               if TestWidth<=RoomR then QtyR:=QtyR+1;
  821.               if TestWidth<=RoomL then QtyL:=QtyL+1;
  822.             end
  823.           else if (SelectMode[j]=ToSubMenu) and LocationWarning then
  824.                  writeln ('No room for SubMenu[',j,']',^G);
  825.          end  { if SelectMode }
  826.       end; { for j }
  827.       if QtyR>=QtyL then LinkDir:=Right else LinkDir:=Left;
  828.     end { with }
  829. {}end; { procedure }
  830. {}procedure AssignLocations (VAR Menu: MenuRec);
  831.   begin
  832.     with Menu do
  833.       for j:=1 to MenuLines do
  834.         case SelectMode[j] of
  835.           ToSubMenu:
  836.             with SubMenu[LinkNum[j]] do
  837.               begin
  838.                 case Menu.LinkDir of
  839.                   Right: Col:=Menu.Col+(Menu.Cols-2);
  840.                   Left:  Col:=Menu.Col-(Cols-2);
  841.                 end;
  842.                 Row:=Menu.Row+j;
  843.                 if (Row+Rows)>CRTrows-2 then Row:=CRTrows-Rows-1;
  844.                 Title:=Menu.Line[j]
  845.               end;
  846.           ToDataWndw:
  847.             begin
  848.               case LinkDir of
  849.                 Right: RoomMax:=CRTcols-(Col+Cols-1);
  850.                 Left:  RoomMax:=Col;
  851.               end;
  852.               with DataWndw[LinkNum[j]] do
  853.                 if Cols>RoomMax then
  854.                   begin
  855.                     RowAlt := ((CRTrows-Rows) shr 1)+1;
  856.                     ColAlt := ((CRTcols-Cols) shr 1)+1
  857.                   end;
  858.             end
  859.         end    { case }
  860. {}end; { procedure }
  861. begin
  862.   for i:=1 to NumOfMainMenus do FindLinkDir (MainMenu[i]);
  863.   for i:=1 to NumOfSubMenus do  FindLinkDir (SubMenu[i]);
  864.   for i:=1 to NumOfMainMenus do AssignLocations (MainMenu[i]);
  865.   for i:=1 to NumOfSubMenus do  AssignLocations (SubMenu[i]);
  866. end;
  867.  
  868. procedure LocateHelpWndws;
  869. begin
  870.   for i:=1 to NumOfHelpWndws do
  871.     with HelpWndw[i] do
  872.     begin
  873.       Row := CRTrows-Rows-RowsBelowHelp+1;
  874.       Col := (CRTcols-Cols) shr 1 + 1
  875.     end
  876. end;
  877.  
  878. procedure ClearPullStats;
  879. begin
  880.  i := (MaxMenuLines+1)*(MaxCharsPerLine+2)+MaxMenuLines*3+19;
  881.  fillchar (MainMenu[1],NumOfMainMenus*i,^@);
  882.  fillchar (SubMenu[1], NumOfSubMenus *i,^@);
  883.  fillchar (DataWndw[1],NumOfDataWndws*((MaxCharsPerLine+1)*2+19),^@);
  884.  fillchar (HelpWndw[1],NumOfHelpWndws*13,^@)
  885. end;
  886.  
  887. procedure InitPull;
  888. begin
  889.   InitWindow (Attr(yellow,black));
  890.   ClearPullStats;
  891.   CRTcols:=CRTcolumns;
  892.   Quit:=false;
  893.   CursorChange (HideCursor,DOScursor);
  894.   MainScrCursor:=DOScursor;
  895.   CursorChange (DOScursor,i);
  896.   GetUserPullStats;
  897.   InitMenuSizeAndColor;
  898.   LocateMainMenus;
  899.   LocateSubMenus;
  900.   LocateHelpWndws;
  901.   GetOverrideStats
  902. end;
  903.