home *** CD-ROM | disk | FTP | other *** search
/ World of Shareware - Software Farm 2 / wosw_2.zip / wosw_2 / PASCAL / MMSRC.ZIP / MENUMGR.PAS < prev    next >
Pascal/Delphi Source File  |  1989-03-13  |  47KB  |  1,285 lines

  1. PROGRAM Menu_Manager;
  2.  
  3. USES
  4.    DOS,      { standard turbo unit }
  5.    CRT,      { standard turbo unit }
  6.    SYSUTIL,  { system type routines }
  7.    QWIK,     { screen i/o routines }
  8.    QWIKUTIL, { supplemental QWIK routines }
  9.    STRUTIL,  { string handling routines }
  10.    IOUTIL,   { bullet-proof user input routines }
  11.    MMUTILS;  { menu manager utilities }
  12.  
  13. CONST
  14.   Shade = '░'; { ░▒▓ }
  15.  
  16.   F1 = 59; { standard F1 key }
  17.   F4 = 62; { standard F4 key }
  18.  
  19.   Prg_Owner = 'ShareWare'; { custom program owner }
  20.   Prg_Title = ' SW MENU MANAGER '; { custom program title }
  21.  
  22.   Max_Nbr_Opts = 13; { maximum number of options per menu }
  23.   Max_Opt_Len = 30;  { maximum option length }
  24.  
  25.   Menu_Delim = ';'; { menu parameter delimiter }
  26.   Opt_Delim = ',';  { option parameter delimiter }
  27.  
  28. TYPE
  29.   Str8 = STRING[8];
  30.   Str12 = STRING[12];
  31.   Str55 = STRING[55];
  32.   Opt_Str = STRING[Max_Opt_Len];
  33.  
  34.   Opt_Types = (Batch,Delim,Menu);
  35.   Status_Types = (Used,Unused);
  36.  
  37.   Opt_Rec = RECORD
  38.               Opt_Type   : Opt_Types;
  39.               Select_Key : CHAR;
  40.               Opt_Name   : Opt_Str;
  41.               Prg_Dir    : Str55;
  42.               Prg_Name   : Str12;
  43.               Parms      : Str55;
  44.               Bat_Name   : Str8;
  45.               Password   : Str8;
  46.               Pause      : BOOLEAN;
  47.               Prompts    : BOOLEAN;
  48.             END;
  49.  
  50.   Menu_Ptr = ^Menu_Rec;
  51.   Menu_Rec = RECORD
  52.                Menu_Name      : Opt_Str;
  53.                Row,Col        : BYTE;
  54.                Opts           : ARRAY [1..Max_Nbr_Opts] OF Opt_Rec;
  55.                Exit_Menu_Name : Opt_Str;
  56.                Cur_Opt_Row    : BYTE;
  57.                Menu_Scr_Ptr   : POINTER;
  58.                Next_Menu_Ptr  : Menu_Ptr;
  59.                Menu_Status    : Status_Types;
  60.              END;
  61.  
  62. VAR
  63.   IO_Error      : INTEGER;    { global IORESULT value }
  64.   Head_Menu_Ptr : Menu_Ptr;   { pointer to first menu }
  65.   Config        : Opt_Str;    { hardware description }
  66.   Config2       : Opt_Str;    { monitor/card description }
  67.   Config3       : Opt_Str;    { screen dimensions description }
  68.   Date_Time_Toggle : BOOLEAN; { show date and time toggle }
  69.   Help_Toggle   : BOOLEAN;    { show help toggle }
  70.   Header_Toggle : BOOLEAN;    { show header toggle }
  71.   Changes       : BOOLEAN;    { any menu changes ? }
  72.   Env_Changes   : BOOLEAN;    { any environment changes ? }
  73.   fgMenu,bgMenu : ARRAY [1..16] OF BYTE; { menu colors }
  74.   fgTitl,bgTitl : ARRAY [1..16] OF BYTE; { menu title colors }
  75.   fgMain,bgMain,              { main screen attribute colors }
  76.   fgCmnd,bgCmnd,              { command line attribute colors }
  77.   fgName,bgName,              { program name attribute colors }
  78.   fgHOpt,bgHOpt,              { all highlighted options }
  79.   fgSlct,bgSlct,              { all quick select keys }
  80.   fgHelp,bgHelp,              { help message boxes }
  81.   fgNErr,bgNErr,              { normal error message boxes }
  82.   fgNote,bgNote,              { note message boxes }
  83.   fgFErr,bgFErr,              { fatal error message boxes }
  84.   fgWarn,bgWarn,              { warning message boxes }
  85.   fgInpt,bgInpt : BYTE;       { all input fields }
  86.  
  87.   {$I INIT_MENU_MANAGER.PAS }
  88.  
  89.   {$I BLD_MENU_LIST.PAS }
  90.  
  91.   PROCEDURE Show_Date_And_Time;
  92.   BEGIN
  93.     QWrite (1,CRTcols-15,-1,CSDS);
  94.     QWrite (1,CRTcols-30,-1,CSTS);
  95.   END;
  96.  
  97.   PROCEDURE Show_Header_Bar;
  98.   BEGIN
  99.     QFill (1,LENGTH(Prg_Title)+1,1,CRTcols-LENGTH(Prg_Title),fgCmnd+bgCmnd,' ');
  100.     QWrite (1,1,fgName+bgName,Prg_Title);
  101.   END;
  102.  
  103.   PROCEDURE Erase_Header_Bar;
  104.   BEGIN
  105.     QFill (1,1,1,CRTcols,fgMain+bgMain,Shade);
  106.   END;
  107.  
  108.   PROCEDURE Init_Scr (fgMain,bgMain : INTEGER);
  109.   BEGIN
  110.     QFill (1,1,CRTrows,CRTcols,fgMain+bgMain,Shade);
  111.     IF Header_Toggle THEN
  112.       Show_Header_Bar;
  113.   END;
  114.  
  115.   PROCEDURE Show_About_Msg;
  116.   VAR Col,Sec_Trigger : BYTE;
  117.       ScrPtr : POINTER;
  118.       c : CHAR;
  119.       Extended : BOOLEAN;
  120.       Hour,Minute,Second,Sec100 : WORD;
  121.   BEGIN
  122.     IF ParamCount > 0 THEN { must be returning from a batch call }
  123.       EXIT;
  124.  
  125.     Col := (CRTcols - 37) DIV 2 + 1;
  126.     Save_Scr (5,Col,16,39,ScrPtr);
  127.     Draw_Box (5,Col,15,37,fgNote+bgNote,Double,-1,Shade,fgMain+bgMain-8);
  128.  
  129.     QWrite (7,Col+1,LightMagenta+bgNote,Justify(Prg_Owner,Center,35,' '));
  130.     QWrite (8,Col+7,fgNErr+bgNote,'M E N U   M A N A G E R');
  131.     QWrite (9,Col+12,fgNErr+bgNote,'Version 1.1.0');
  132.     QWrite (10,Col+14,fgNErr+bgNote,'6-Dec-88');
  133.     QWrite (12,Col+9,fgNErr+bgNote,'Copyright (c) 1989');
  134.     QWrite (13,Col+5,fgNErr+bgNote,'Creative Software Solutions');
  135.     QWrite (15,Col+1,fgNErr+bgNote,Justify(Config,Center,35,' '));
  136.     QWrite (16,Col+1,fgNErr+bgNote,Justify(Config2,Center,35,' '));
  137.     QWrite (17,Col+1,fgNErr+bgNote,Justify(Config3,Center,35,' '));
  138.  
  139.     GetTime (Hour,Minute,Second,Sec100);
  140.     IF Second > 54 THEN
  141.       Sec_Trigger := Second + 6 - 60
  142.     ELSE
  143.       Sec_Trigger := Second + 6;
  144.     REPEAT
  145.       GetTime (Hour,Minute,Second,Sec100);
  146.       IF Date_Time_Toggle THEN
  147.         Show_Date_And_Time;
  148.     UNTIL KeyPressed OR (Second = Sec_Trigger);
  149.     IF KeyPressed THEN { read the key in, just to clear the buffer }
  150.       Wait_For_Key (c,Extended);
  151.  
  152.     Show_Scr (5,Col,16,39,ScrPtr);
  153.   END;
  154.  
  155.   PROCEDURE Manage_Menus;
  156.   VAR Cur_Menu_Ptr,Temp_Menu_Ptr : Menu_Ptr;
  157.       Batch_File,User_Batch_File : TEXT;
  158.       c : CHAR;
  159.       Extended,Option_Selected,Menu_Selected,Abort,Save_Scr_Ptr : BOOLEAN;
  160.       y,Old_y,x,Help_Row,Help_Col,Help_Rows,Help_Cols,Menu_Idx,i : BYTE;
  161.       Menu_Nbr,Last_Menu_Nbr : WORD;
  162.       Help_Scr_Ptr : POINTER;
  163.       Cur_Dir,Opt_Parms : STRING;
  164.       Saved_Opt : Opt_Rec; { hold buffer for cutting and pasting }
  165.  
  166.     FUNCTION Get_Help_Row (Cur_Menu_Ptr : Menu_Ptr) : BYTE;
  167.     BEGIN
  168.       IF Cur_Menu_Ptr^.Row < 5 THEN { help goes on bottom half of screen with buffer }
  169.         Get_Help_Row := 19
  170.       ELSE
  171.         IF Cur_Menu_Ptr^.Row IN [5,6] THEN { help goes on bottom half of screen with no buffer }
  172.           Get_Help_Row := 20
  173.         ELSE
  174.           IF Header_Toggle THEN { we've got one less line to use }
  175.             IF Cur_Menu_Ptr^.Row > 8 THEN { help goes on top half of screen with buffer }
  176.               Get_Help_Row := 3
  177.             ELSE { help goes on top half of screen with no buffer }
  178.               Get_Help_Row := 2
  179.           ELSE
  180.             IF Cur_Menu_Ptr^.Row > 7 THEN  { help goes on top half of screen with buffer }
  181.               Get_Help_Row := 2
  182.             ELSE
  183.               IF Cur_Menu_Ptr^.Row = 7 THEN { help goes on top half of screen with no buffer }
  184.                 Get_Help_Row := 1;
  185.     END;
  186.  
  187.     FUNCTION Opt_Question (Qst_Title,Qst_Str : STRING; Help_Nbr,Help_Row,Help_Col : BYTE) : BYTE;
  188.     CONST Return = #13;
  189.           Escape = #27;
  190.           Start_Row = 7;
  191.     VAR Opt_Nbr,Col,Qst_Str_Len : BYTE;
  192.         Done,Extended : BOOLEAN;
  193.         Qst_Scr_Ptr : POINTER;
  194.         c : CHAR;
  195.     BEGIN
  196.       IF Help_Toggle THEN
  197.         Erase_Help;
  198.       IF Scan(Qst_Str,Forwards,EQ,'[') = LENGTH(Qst_Str) THEN { there are no choices }
  199.         Qst_Str := CONCAT(Qst_Str,' ? [YES] [NO]');
  200.       Col := ((CRTcols-(LENGTH(Qst_Str)+4))DIV 2)+1;
  201.       Qst_Str_Len := LENGTH(Qst_Str);
  202.       Save_Scr (Start_Row,Col,5,Qst_Str_Len+4,Qst_Scr_Ptr);
  203.       Draw_Box (Start_Row,Col,4,Qst_Str_Len+2,Black+bgNote,No_Border,-1,Shade,fgMain+bgMain-8);
  204.       QWrite (Start_Row,Col,(bgNote DIV 16)+bBlack+8,Justify(Qst_Title,Center,Qst_Str_Len+2,' '));
  205.       QWrite (Start_Row+2,Col+1,fgNote+bgNote,Qst_Str);
  206.       IF Help_Toggle THEN
  207.         Show_Help (Help_Nbr,Help_Row,Help_Col,fgHelp+bgHelp);
  208.       Sound_Bell;
  209.       Opt_Nbr := 1;
  210.       Done := FALSE;
  211.       REPEAT
  212.         HSelect (Start_Row+2,Col+1,0,fgInpt+bgInpt,Opt_Nbr,'[',']',c,Extended);
  213.         IF Extended THEN
  214.           CASE ORD(c) OF
  215.             F1 : BEGIN
  216.                    Help_Toggle := NOT Help_Toggle;
  217.                    Env_Changes := TRUE;
  218.                    IF Help_Toggle THEN
  219.                      Show_Help (Help_Nbr,Help_Row,Help_Col,fgHelp+bgHelp)
  220.                    ELSE
  221.                      Erase_Help;
  222.                  END;
  223.             61 : Done := TRUE;
  224.             ELSE
  225.               Sound_Bell;
  226.           END
  227.         ELSE
  228.           IF c IN [Return,Escape] THEN
  229.             Done := TRUE
  230.           ELSE
  231.             Sound_Bell;
  232.       UNTIL Done;
  233.       IF c = Return THEN
  234.         Opt_Question := Opt_Nbr
  235.       ELSE
  236.         Opt_Question := 0;
  237.       IF Help_Toggle THEN
  238.         Erase_Help;
  239.       Show_Scr (Start_Row,Col,5,Qst_Str_Len+4,Qst_Scr_Ptr);
  240.       IF Help_Toggle THEN
  241.         Show_Help (0,Get_Help_Row(Cur_Menu_Ptr),3,fgHelp+bgHelp);
  242.     END;
  243.  
  244.     FUNCTION Str_Question (Qst_Title,Qst_Str : STRING; Help_Nbr,Help_Row,Help_Col : BYTE;
  245.                        VAR Reply : STRING; Inp_Chr_Type : BOOLEAN) : BOOLEAN;
  246.     CONST Null = #0;
  247.           Return = #13;
  248.           Escape = #27;
  249.           Start_Row = 7;
  250.     VAR Done,Extended : BOOLEAN;
  251.         Qst_Scr_Ptr : POINTER;
  252.         c : CHAR;
  253.         Col,Qst_Str_Len,Start_Inp_Fld_Col,Inp_Fld_Len : Byte;
  254.         Def_Inp_Str : STRING;
  255.     BEGIN
  256.       Str_Question := FALSE;
  257.       IF Help_Toggle THEN
  258.         Erase_Help;
  259.       Qst_Str_Len := LENGTH(Qst_Str);
  260.       Col := ((CRTcols-(Qst_Str_Len+4))DIV 2)+1;
  261.       Save_Scr (Start_Row,Col,5,Qst_Str_Len+4,Qst_Scr_Ptr);
  262.       Draw_Box (Start_Row,Col,4,Qst_Str_Len+2,Black+bgNote,No_Border,-1,Shade,fgMain+bgMain-8);
  263.       QWrite (Start_Row,Col,(bgNote DIV 16)+bBlack+8,Justify(Qst_Title,Center,Qst_Str_Len+2,' '));
  264.       QWrite (Start_Row+2,Col+1,fgNote+bgNote,Qst_Str);
  265.       IF Help_Toggle THEN
  266.         Show_Help (Help_Nbr,Help_Row,Help_Col,fgHelp+bgHelp);
  267.       Start_Inp_Fld_Col := Scan(Qst_Str,Forwards,EQ,'[') + 2;
  268.       Inp_Fld_Len := Scan(Qst_Str,Forwards,EQ,']') - Start_Inp_Fld_Col + 1;
  269.       Def_Inp_Str := Reply;
  270.       Sound_Bell;
  271.       Done := FALSE;
  272.       REPEAT
  273.         QWrite (Start_Row+2,Col+Start_Inp_Fld_Col,-1,Justify(Def_Inp_Str,Left,Inp_Fld_Len,' '));
  274.         Input (Inp_Fld_Len,Start_Row+2,Col+Start_Inp_Fld_Col,fgInpt+bgInpt,Reply,c,Extended,Inp_Chr_Type);
  275.         IF Extended THEN
  276.           CASE ORD(c) OF
  277.             F1 : BEGIN
  278.                    Help_Toggle := NOT Help_Toggle;
  279.                    Env_Changes := TRUE;
  280.                    IF Help_Toggle THEN
  281.                      Show_Help (Help_Nbr,Help_Row,Help_Col,fgHelp+bgHelp)
  282.                    ELSE
  283.                      Erase_Help;
  284.                  END;
  285.             61 : Done := TRUE;
  286.             ELSE
  287.               Sound_Bell;
  288.           END
  289.         ELSE
  290.           IF c = Null THEN
  291.             BEGIN
  292.               Str_Question := TRUE;
  293.               Done := TRUE;
  294.             END
  295.           ELSE
  296.             IF c IN [Return,Escape] THEN
  297.               Done := TRUE
  298.             ELSE
  299.               Sound_Bell;
  300.       UNTIL Done;
  301.       IF Help_Toggle THEN
  302.         Erase_Help;
  303.       Show_Scr (Start_Row,Col,5,Qst_Str_Len+4,Qst_Scr_Ptr);
  304.       IF Help_Toggle THEN
  305.         Show_Help (0,Get_Help_Row(Cur_Menu_Ptr),3,fgHelp+bgHelp); { menu help }
  306.     END;
  307.  
  308.     PROCEDURE Init_Menu (VAR Temp_Menu_Ptr : Menu_Ptr; Name : STRING; y,x : BYTE);
  309.     BEGIN
  310.       NEW (Temp_Menu_Ptr);
  311.       WITH Temp_Menu_Ptr^ DO
  312.         BEGIN
  313.           Menu_Name := Name;
  314.           Row := y;
  315.           Col := x;
  316.           FILLCHAR (Opts,SIZEOF(Opts),CHR(0));
  317.           Exit_Menu_Name := ''; { will modify dynamically }
  318.           Cur_Opt_Row := 0; { will modify dynamically }
  319.           Menu_Scr_Ptr := NIL;
  320.           Next_Menu_Ptr := NIL;
  321.         END;
  322.     END;
  323.  
  324.     PROCEDURE Show_Menu (VAR Cur_Menu_Ptr : Menu_Ptr);
  325.     VAR i : BYTE;
  326.     BEGIN
  327.       WITH Cur_Menu_Ptr^ DO
  328.         BEGIN
  329.           Save_Scr (Row,Col,Max_Nbr_Opts+2,Max_Opt_Len+8,Menu_Scr_Ptr);
  330.           Draw_Box (Row,Col,Max_Nbr_Opts+1,Max_Opt_Len+6,fgMenu[Menu_Idx]+bgMenu[Menu_Idx],No_Border,-1,Shade,
  331.                       fgMain+bgMain-8);
  332.           QWrite (Row,Col,fgTitl[Menu_Idx]+bgTitl[Menu_Idx],Justify(Menu_Name,Center,Max_Opt_Len+6,' '));
  333.           FOR i := 1 TO Max_Nbr_Opts DO
  334.             WITH Opts[i] DO
  335.               IF Opt_Type = Delim THEN
  336.                 QWrite (Row+i,Col+1,-1,Justify(Strip(Opt_Name,Ends,EQ,' '),Center,Max_Opt_Len+4,'─'))
  337.               ELSE
  338.                 QWrite (Row+i,Col+1,-1,CONCAT(Select_Key,'   ',Opt_Name));
  339.         END;
  340.       IF Help_Toggle THEN
  341.         Show_Help (0,Get_Help_Row(Cur_Menu_Ptr),3,fgHelp+bgHelp); { menu help }
  342.     END;
  343.  
  344.     PROCEDURE Rename_Menu;
  345.     VAR Temp_Menu_Ptr : Menu_Ptr;
  346.         Reply : STRING;
  347.         i : Byte;
  348.         Done : BOOLEAN;
  349.     BEGIN
  350.       Done := FALSE;
  351.       REPEAT
  352.         Reply := Cur_Menu_Ptr^.Menu_Name;
  353.         IF Str_Question('Rename Menu',CONCAT('Menu name ? [',Make_String(Max_Opt_Len,' '),']'),2,13,3,Reply,Visible_Chrs) THEN
  354.           BEGIN
  355.             Temp_Menu_Ptr := Head_Menu_Ptr;
  356.             WHILE (Temp_Menu_Ptr^.Menu_Name <> Reply) AND
  357.                   (Temp_Menu_Ptr^.Next_Menu_Ptr <> NIL) DO
  358.               Temp_Menu_Ptr := Temp_Menu_Ptr^.Next_Menu_Ptr;
  359.             IF Temp_Menu_Ptr^.Menu_Name = Reply THEN
  360.               Show_Error (4,9,9,fgNErr+bgNErr) { DUPLICATE MENU NAME }
  361.             ELSE
  362.               BEGIN
  363.                 Done := TRUE;
  364.                 Changes := TRUE;
  365.                 Temp_Menu_Ptr := Head_Menu_Ptr;
  366.                 REPEAT
  367.                   FOR i := 1 TO Max_Nbr_Opts DO
  368.                     WITH Temp_Menu_Ptr^.Opts[i] DO
  369.                       IF (Opt_Type = Menu) AND
  370.                          (Opt_Name = Cur_Menu_Ptr^.Menu_Name) THEN
  371.                         Opt_Name := Reply;
  372.                   Temp_Menu_Ptr := Temp_Menu_Ptr^.Next_Menu_Ptr;
  373.                 UNTIL Temp_Menu_Ptr = NIL;
  374.                 Cur_Menu_Ptr^.Menu_Name := Reply;
  375.                 WITH Cur_Menu_Ptr^ DO
  376.                   QWrite (Row,Col,fgTitl[Menu_Idx]+bgTitl[Menu_Idx],Justify(Menu_Name,Center,Max_Opt_Len+6,' '));
  377.               END;
  378.           END
  379.         ELSE
  380.           Done := TRUE;
  381.       UNTIL Done;
  382.     END;
  383.  
  384.     PROCEDURE Change_Time;
  385.     VAR Done : BOOLEAN;
  386.         Reply : STRING;
  387.         Hour,Minute,Second,Sec100 : WORD;
  388.  
  389.       FUNCTION Valid_Time (s : STRING; VAR Hour,Minute,Second : WORD) : BOOLEAN;
  390.       VAR i,Fld_Cnt : BYTE;
  391.           Error : INTEGER;
  392.           Fld : STRING;
  393.           Delim : CHAR;
  394.           Hour2,Minute2,Second2 : WORD;
  395.       BEGIN
  396.         Valid_Time := FALSE;
  397.         s := CONCAT(s,':');
  398.  
  399.         IF LENGTH(s) < 2 THEN
  400.           EXIT;
  401.  
  402.         Fld := '';
  403.         Fld_Cnt := 0;
  404.         FOR i := 1 TO LENGTH(s) DO
  405.           IF s[i] IN ['0'..'9'] THEN
  406.             Fld := CONCAT(Fld,s[i])
  407.           ELSE
  408.             BEGIN
  409.               Fld_Cnt := Fld_Cnt + 1;
  410.               CASE Fld_Cnt OF
  411.                 1 : VAL (Fld,Hour2,Error);
  412.                 2 : VAL (Fld,Minute2,Error);
  413.                 3 : VAL (Fld,Second2,Error);
  414.                 ELSE
  415.                   EXIT;
  416.               END;
  417.               Fld := '';
  418.             END;
  419.  
  420.         IF Fld_Cnt < 3 THEN
  421.           EXIT;
  422.  
  423.         IF (Hour2 < 0) OR (Hour2 > 23) THEN
  424.           EXIT;
  425.         IF (Minute2 < 0) OR (Minute2 > 59) THEN
  426.           EXIT;
  427.         IF (Second2 < 0) OR (Second2 > 59) THEN
  428.           EXIT;
  429.  
  430.         Hour := Hour2;
  431.         Minute := Minute2;
  432.         Second := Second2;
  433.  
  434.         Valid_Time := TRUE;
  435.       END;
  436.  
  437.     BEGIN { Change_Time }
  438.       GetTime (Hour,Minute,Second,Sec100);
  439.       Done := FALSE;
  440.       REPEAT
  441.         Reply := CONCAT(Justify(CIS(Hour),Right,2,'0'),':',
  442.                    Justify(CIS(Minute),Right,2,'0'),':',Justify(CIS(Second),Right,2,'0'));
  443.         IF Str_Question('Change Time',CONCAT('System time ? [',Reply,']'),4,14,3,Reply,Visible_Chrs) THEN
  444.           IF Valid_Time(Reply,Hour,Minute,Second) THEN
  445.             BEGIN
  446.               SetTime (Hour,Minute,Second,0);
  447.               Done := TRUE;
  448.             END
  449.           ELSE
  450.             Show_Error (2,9,9,fgNErr+bgNErr) { INVALID TIME FORMAT }
  451.         ELSE
  452.           Done := TRUE;
  453.       UNTIL Done;
  454.     END;
  455.  
  456.     PROCEDURE Swap_Options (Cur_Row : BYTE) ;
  457.     CONST Return = #13;
  458.           Escape = #27;
  459.           Null = #0;
  460.     VAR Done,Extended,Abort : BOOLEAN;
  461.         Old_Row,New_Row : BYTE;
  462.         c : CHAR;
  463.         Cur_Opt,AttrStr,s : STRING;
  464.         Temp_Opt : Opt_Rec;
  465.  
  466.       PROCEDURE Show_Brackets (Row,Attr : BYTE);
  467.       BEGIN
  468.         QWrite (Row,Cur_Menu_Ptr^.Col,Attr,CHR(16));
  469.         QWrite (Row,Cur_Menu_Ptr^.Col+Max_Opt_Len+5,Attr,CHR(17));
  470.       END;
  471.  
  472.       PROCEDURE Erase_Brackets (Row : BYTE);
  473.       BEGIN
  474.         QWrite (Row,Cur_Menu_Ptr^.Col,fgMenu[Menu_Idx]+bgMenu[Menu_Idx],' ');
  475.         QWrite (Row,Cur_Menu_Ptr^.Col+Max_Opt_Len+5,fgMenu[Menu_Idx]+bgMenu[Menu_Idx],' ');
  476.       END;
  477.  
  478.     BEGIN { Swap_Options }
  479.       IF Help_Toggle THEN
  480.         Erase_Help;
  481.       IF Help_Toggle THEN
  482.         Show_Help (6,Get_Help_Row(Cur_Menu_Ptr),2,fgHelp+bgHelp); { swap help }
  483.       Abort := FALSE;
  484.       Done := FALSE;
  485.       New_Row := Cur_Row;
  486.       Old_Row := New_Row;
  487.       Get_Scr_Str (Cur_Row,Cur_Menu_Ptr^.Col+1,Max_Opt_Len+4,Cur_Opt,AttrStr);
  488.       QAttr (Cur_Row,Cur_Menu_Ptr^.Col+1,1,Max_Opt_Len+4,fgHOpt+bgHOpt);
  489.       Show_Brackets (Cur_Row,fgHOpt+bgHOpt);
  490.       WITH Cur_Menu_Ptr^ DO
  491.         BEGIN
  492.           REPEAT
  493.             IF New_Row <> Old_Row THEN
  494.               BEGIN
  495.                 Get_Scr_Str (Cur_Row,Col+1,Max_Opt_Len+4,s,AttrStr);
  496.                 QWrite (Old_Row,Col+1,fgMenu[Menu_Idx]+bgMenu[Menu_Idx],s);
  497.                 Erase_Brackets (Old_Row);
  498.                 Get_Scr_Str (New_Row,Col+1,Max_Opt_Len+4,s,AttrStr);
  499.                 QWrite (Cur_Row,Col+1,fgHOpt+bgHOpt+Blink,s);
  500.                 { could turn on the Cur_Row brackets here... }
  501.                 QWrite (New_Row,Col+1,fgHOpt+bgHOpt,Cur_Opt);
  502.                 Show_Brackets (New_Row,fgHOpt+bgHOpt);
  503.                 Old_Row := New_Row;
  504.               END;
  505.             REPEAT
  506.               IF Date_Time_Toggle THEN
  507.                 Show_Date_And_Time;
  508.             UNTIL KeyPressed;
  509.             Wait_For_Key (c,Extended);
  510.             IF Extended THEN
  511.               CASE ORD(c) OF
  512.                 F1 : BEGIN                              { F1 - help toggle }
  513.                        Help_Toggle := NOT Help_Toggle;
  514.                        Env_Changes := TRUE;
  515.                        IF Help_Toggle THEN
  516.                          Show_Help (6,Get_Help_Row(Cur_Menu_Ptr),2,fgHelp+bgHelp) { swap help }
  517.                        ELSE
  518.                          Erase_Help;
  519.                      END;
  520.                 61 : Abort := TRUE;                     { F3 - exit }
  521.                 72,                                     { left-arrow }
  522.                 75 : IF New_Row > Row+1 THEN            { up-arrow }
  523.                        New_Row := New_Row - 1
  524.                      ELSE
  525.                        New_Row := Row + Max_Nbr_Opts;
  526.                 77,                                     { right-arrow }
  527.                 80 : IF New_Row < Row+Max_Nbr_Opts THEN { down-arrow }
  528.                        New_Row := New_Row + 1
  529.                      ELSE
  530.                        New_Row := Row + 1;
  531.                 71 : New_Row := Row + 1;                { home }
  532.                 79 : New_Row := Row + Max_Nbr_Opts;     { end }
  533.                 ELSE
  534.                   Sound_Bell;
  535.               END
  536.             ELSE
  537.               CASE ORD(c) OF
  538.                 13 : Done := TRUE;  { enter }
  539.                 27 : Abort := TRUE; { escape }
  540.                 ELSE { any other non-extended key }
  541.                   Sound_Bell;
  542.               END;
  543.           UNTIL Done OR Abort;
  544.           Erase_Brackets (Cur_Row);
  545.           Erase_Brackets (New_Row);
  546.           IF Done THEN { we may have a valid swap }
  547.             BEGIN
  548.               IF New_Row <> Cur_Row THEN
  549.                 BEGIN
  550.                   Changes := TRUE;
  551.                   Temp_Opt := Opts[New_Row-Row];
  552.                   Opts[New_Row-Row] := Opts[Cur_Row-Row];
  553.                   Opts[Cur_Row-Row] := Temp_Opt;
  554.                   QAttr (Cur_Row,Col+1,1,Max_Opt_Len+4,fgHOpt+bgHOpt);
  555.                   QAttr (New_Row,Col+1,1,Max_Opt_Len+4,fgMenu[Menu_Idx]+bgMenu[Menu_Idx]);
  556.                 END;
  557.             END
  558.           ELSE
  559.             BEGIN
  560.               Get_Scr_Str (Cur_Row,Col+1,Max_Opt_Len+4,s,AttrStr);
  561.               QWrite (New_Row,Col+1,fgMenu[Menu_Idx]+bgMenu[Menu_Idx],s);
  562.               QWrite (Cur_Row,Col+1,fgHOpt+bgHOpt,Cur_Opt);
  563.             END;
  564.         END;
  565.       IF Help_Toggle THEN
  566.         Erase_Help;
  567.       IF Help_Toggle THEN
  568.         Show_Help (0,Get_Help_Row(Cur_Menu_Ptr),3,fgHelp+bgHelp); { menu help }
  569.     END;
  570.  
  571.     PROCEDURE Change_Date;
  572.     CONST Return = #13;
  573.           Escape = #27;
  574.           Null = #0;
  575.     VAR Done,Extended : BOOLEAN;
  576.         Date_Scr_Ptr : POINTER;
  577.         Reply : STRING;
  578.         c : CHAR;
  579.         Year,Month,Day,DayofWeek : WORD;
  580.  
  581.       FUNCTION Valid_Date (s : STRING; VAR Month,Day,Year : WORD) : BOOLEAN;
  582.       VAR i,Fld_Cnt : BYTE;
  583.           Error : INTEGER;
  584.           Fld : STRING;
  585.           Delim : CHAR;
  586.           Month2,Day2,Year2 : WORD;
  587.       BEGIN
  588.         Valid_Date := FALSE;
  589.  
  590.         IF LENGTH(s) < 2 THEN
  591.           EXIT;
  592.  
  593.         s := CONCAT(s,'/');
  594.  
  595.         Fld := '';
  596.         Fld_Cnt := 0;
  597.         FOR i := 1 TO LENGTH(s) DO
  598.           IF s[i] IN ['0'..'9'] THEN
  599.             Fld := CONCAT(Fld,s[i])
  600.           ELSE
  601.             BEGIN
  602.               Fld_Cnt := Fld_Cnt + 1;
  603.               CASE Fld_Cnt OF
  604.                 1 : VAL (Fld,Month2,Error);
  605.                 2 : VAL (Fld,Day2,Error);
  606.                 3 : VAL (Fld,Year2,Error);
  607.                 ELSE
  608.                   EXIT;
  609.               END;
  610.               Fld := '';
  611.             END;
  612.  
  613.         IF Fld_Cnt < 3 THEN
  614.           EXIT;
  615.  
  616.         IF (Month2 < 1) OR (Month2 > 12) THEN
  617.           EXIT;
  618.         IF (Day2 < 1) OR (Day2 > 31) THEN
  619.           EXIT;
  620.         IF (Year2 < 0) OR (Year2 > 99) THEN
  621.           EXIT;
  622.  
  623.         Month := Month2;
  624.         Day := Day2;
  625.         Year := Year2;
  626.  
  627.         Valid_Date := TRUE;
  628.       END;
  629.  
  630.     BEGIN { Change_Date }
  631.       GetDate (Year,Month,Day,DayofWeek);
  632.       Done := FALSE;
  633.       REPEAT
  634.         Reply := CONCAT(Justify(CIS(Month),Right,2,'0'),'/',
  635.                    Justify(CIS(Day),Right,2,'0'),'/',Justify(CIS(Year-1900),Right,2,'0'));
  636.         IF Str_Question('Change Date',CONCAT('System date ? [',Reply,']'),3,14,3,Reply,Visible_Chrs) THEN
  637.           IF Valid_Date(Reply,Month,Day,Year) THEN
  638.             BEGIN
  639.               SetDate (Year+1900,Month,Day);
  640.               Done := TRUE;
  641.             END
  642.           ELSE
  643.             Show_Error (3,9,9,fgNErr+bgNErr) { INVALID DATE FORMAT }
  644.         ELSE
  645.           Done := TRUE;
  646.       UNTIL Done;
  647.     END;
  648.  
  649.     {$I SETTINGS_EDITOR.PAS }
  650.  
  651.     FUNCTION Chk_Password (Opt_Password : STRING) : BOOLEAN;
  652.     VAR Done : BOOLEAN;
  653.         Reply : STRING;
  654.     BEGIN
  655.       IF LENGTH(Opt_Password) = 0 THEN
  656.         BEGIN
  657.           Chk_Password := TRUE;
  658.           EXIT;
  659.         END;
  660.       Chk_Password := FALSE;
  661.       Done := FALSE;
  662.       REPEAT
  663.        Reply := Make_String(8,' ');
  664.        IF Str_Question('Password','Enter Option Password (it will not show on screen) [        ]',
  665.                         9,13,3,Reply,Invisible_Chrs) THEN
  666.          IF Reply = Opt_Password THEN
  667.            BEGIN
  668.              Chk_Password := TRUE;
  669.              Done := TRUE;
  670.            END
  671.          ELSE
  672.            Show_Error (5,8,9,fgNErr+bgNErr) { INCORRECT PASSWORD }
  673.        ELSE
  674.          Done := TRUE;
  675.       UNTIL Done;
  676.     END;
  677.  
  678.     {$I OPTION_EDITOR.PAS }
  679.  
  680.     PROCEDURE Erase_Menu (Cur_Menu_Ptr : Menu_Ptr);
  681.     BEGIN
  682.       IF Help_Toggle THEN
  683.         Erase_Help;
  684.       WITH Cur_Menu_Ptr^ DO
  685.         Show_Scr (Row,Col,Max_Nbr_Opts+2,Max_Opt_Len+8,Menu_Scr_Ptr);
  686.     END;
  687.  
  688.     PROCEDURE Add_Menu (Temp_Menu_Ptr : Menu_Ptr);
  689.     VAR Last_Menu_Ptr : Menu_Ptr;
  690.     BEGIN
  691.       Last_Menu_Ptr := Head_Menu_Ptr;
  692.       WHILE Last_Menu_Ptr^.Next_Menu_Ptr <> NIL DO
  693.         Last_Menu_Ptr := Last_Menu_Ptr^.Next_Menu_Ptr;
  694.       Last_Menu_Ptr^.Next_Menu_Ptr := Temp_Menu_Ptr;
  695.     END;
  696.  
  697.     PROCEDURE Set_Help;
  698.     BEGIN
  699.       Help_Toggle := NOT Help_Toggle;
  700.       Env_Changes := TRUE;
  701.       IF Help_Toggle THEN
  702.         Show_Help (0,Get_Help_Row(Cur_Menu_Ptr),3,fgHelp+bgHelp) { menu help }
  703.       ELSE
  704.         Erase_Help;
  705.     END;
  706.  
  707.     PROCEDURE Set_Exit (VAR Cur_Menu_Ptr : Menu_Ptr);
  708.     VAR i : BYTE;
  709.     BEGIN
  710.       IF Menu_Nbr > 1 THEN { can exit current menu }
  711.         WITH Cur_Menu_Ptr^ DO
  712.           BEGIN
  713.             Temp_Menu_Ptr := Head_Menu_Ptr; { previous menu is before }
  714.             WHILE (Temp_Menu_Ptr^.Next_Menu_Ptr <> NIL) AND
  715.                   (Temp_Menu_Ptr^.Menu_Name <> Exit_Menu_Name) DO
  716.               Temp_Menu_Ptr := Temp_Menu_Ptr^.Next_Menu_Ptr;
  717.             IF Temp_Menu_Ptr^.Menu_Name = Exit_Menu_Name THEN { found the named menu }
  718.               BEGIN
  719.                 Exit_Menu_Name := ''; { reset the current exit pointer }
  720.                 Cur_Opt_Row := 0; { reset the row pointer }
  721.                 Erase_Menu (Cur_Menu_Ptr);
  722.                 Cur_Menu_Ptr := Temp_Menu_Ptr;
  723.                 Menu_Selected := TRUE;
  724.                 Last_Menu_Nbr := Menu_Nbr;
  725.                 Menu_Nbr := Menu_Nbr - 1;
  726.                 Menu_Idx := Menu_Nbr MOD 16;
  727.                 IF Header_Toggle THEN
  728.                   WITH Cur_Menu_Ptr^ DO
  729.                     IF Row = 1 THEN { have an overlap problem; need to adjust starting menu row }
  730.                       BEGIN
  731.                         Erase_Header_Bar;
  732.                         Erase_Menu (Cur_Menu_Ptr);
  733.                         Changes := TRUE;
  734.                         Row := Row + 1;
  735.                         Show_Menu (Cur_Menu_Ptr);
  736.                         IF Cur_Opt_Row > 0 THEN
  737.                           Cur_Opt_Row := Cur_Opt_Row + 1;
  738.                         Show_Header_Bar;
  739.                       END;
  740.                 IF Help_Toggle THEN
  741.                   Show_Help (0,Get_Help_Row(Cur_Menu_Ptr),3,fgHelp+bgHelp);
  742.               END
  743.             ELSE
  744.               Show_Fatal (10,7,20,fgFErr+bgFErr); { fatal error of some sort }
  745.           END
  746.       ELSE { may want to exit to dos... }
  747.         BEGIN
  748.           i := Opt_Question('Quit Menu Manager','Are you sure you want to quit and return to DOS',1,14,14);
  749.           IF i IN [0,2] THEN
  750.             Abort := FALSE
  751.           ELSE
  752.             Abort := TRUE;
  753.         END;
  754.     END;
  755.  
  756.     PROCEDURE Set_Header (VAR Cur_Menu_Ptr : Menu_Ptr);
  757.     BEGIN
  758.       Header_Toggle := NOT Header_Toggle;
  759.       Env_Changes := TRUE;
  760.       WITH Cur_Menu_Ptr^ DO
  761.         IF Header_Toggle THEN
  762.           BEGIN
  763.             IF Row = 1 THEN
  764.               BEGIN
  765.                 Changes := TRUE;
  766.                 Erase_Menu (Cur_Menu_Ptr);
  767.                 Row := Row + 1;
  768.                 Show_Menu (Cur_Menu_Ptr);
  769.                 y := y + 1;
  770.                 Old_y := Old_y + 1;
  771.               END;
  772.             Date_Time_Toggle := TRUE;
  773.             Show_Header_Bar;
  774.           END
  775.         ELSE
  776.           BEGIN
  777.             Date_Time_Toggle := FALSE; { force date and time off }
  778.             Erase_Header_Bar;
  779.             IF Help_Toggle THEN
  780.               BEGIN
  781.                 Erase_Help;
  782.                 Show_Help (0,Get_Help_Row(Cur_Menu_Ptr),3,fgHelp+bgHelp);
  783.               END;
  784.           END;
  785.     END;
  786.  
  787.     PROCEDURE Set_Date_Time;
  788.     BEGIN
  789.       IF NOT(Header_Toggle) AND NOT(Date_Time_Toggle) THEN
  790.         BEGIN
  791.           Sound_Bell;
  792.           (* error message? *)
  793.           EXIT;
  794.         END;
  795.       Date_Time_Toggle := NOT Date_Time_Toggle;
  796.       Env_Changes := TRUE;
  797.       IF NOT Date_Time_Toggle THEN
  798.         QWrite (1,CRTcols-30,-1,Make_String(CRTcols-(CRTcols-30)+1,' '));
  799.     END;
  800.  
  801.     PROCEDURE Up_Arrow (VAR y : BYTE);
  802.     BEGIN
  803.       WITH Cur_Menu_Ptr^ DO
  804.         IF y > Row+1 THEN
  805.           y := y - 1
  806.         ELSE
  807.           y := Row + Max_Nbr_Opts;
  808.     END;
  809.  
  810.     PROCEDURE Down_Arrow (VAR y : BYTE);
  811.     BEGIN
  812.       WITH Cur_Menu_Ptr^ DO
  813.         IF y < Row+Max_Nbr_Opts THEN
  814.           y := y + 1
  815.         ELSE
  816.           y := Row + 1;
  817.     END;
  818.  
  819.     PROCEDURE Move_Menu_Left (VAR Cur_Menu_Ptr : Menu_Ptr);
  820.     BEGIN
  821.       WITH Cur_Menu_Ptr^ DO
  822.         IF Col > 1 THEN { ctrl-left-arrow }
  823.           BEGIN
  824.             Erase_Menu (Cur_Menu_Ptr);
  825.             Col := Col - 1;
  826.             Changes := TRUE;
  827.             Show_Menu (Cur_Menu_Ptr);
  828.           END;
  829.     END;
  830.  
  831.     PROCEDURE Move_Menu_Right (VAR Cur_Menu_Ptr : Menu_Ptr);
  832.     BEGIN
  833.       WITH Cur_Menu_Ptr^ DO
  834.         IF Col < CRTcols-(Max_Opt_Len+8)+1 THEN { ctrl-right-arrow }
  835.           BEGIN
  836.             Erase_Menu (Cur_Menu_Ptr);
  837.             Col := Col + 1;
  838.             Changes := TRUE;
  839.             Show_Menu (Cur_Menu_Ptr);
  840.           END;
  841.     END;
  842.  
  843.     PROCEDURE Move_Menu_Down (VAR Cur_Menu_Ptr : Menu_Ptr);
  844.     BEGIN
  845.       WITH Cur_Menu_Ptr^ DO
  846.         IF Row < CRTrows-(Max_Nbr_Opts+1) THEN { ctrl-pgdn }
  847.           BEGIN
  848.             Erase_Menu (Cur_Menu_Ptr);
  849.             Row := Row + 1;
  850.             Changes := TRUE;
  851.             Show_Menu (Cur_Menu_Ptr);
  852.             y := y + 1;
  853.             Old_y := Old_y + 1;
  854.           END;
  855.     END;
  856.  
  857.     PROCEDURE Move_Menu_Up (VAR Cur_Menu_Ptr : Menu_Ptr);
  858.     BEGIN
  859.       WITH Cur_Menu_Ptr^ DO
  860.         IF (Header_Toggle AND (Row > 2)) OR
  861.            (NOT(Header_Toggle) AND (Row > 1)) THEN
  862.           BEGIN
  863.             Erase_Menu (Cur_Menu_Ptr);
  864.             Row := Row - 1;
  865.             Changes := TRUE;
  866.             Show_Menu (Cur_Menu_Ptr);
  867.             y := y - 1;
  868.             Old_y := Old_y - 1;
  869.          END;
  870.     END;
  871.  
  872.     PROCEDURE Chk_Enter (VAR Cur_Menu_Ptr : Menu_Ptr);
  873.     VAR Temp_Menu_Ptr : Menu_Ptr;
  874.     BEGIN
  875.       WITH Cur_Menu_Ptr^ DO
  876.         WITH Opts[y-Row] DO
  877.         CASE Opt_Type OF
  878.           Batch : BEGIN
  879.                     IF LENGTH(Opt_Name) = 0 THEN
  880.                       IF y < Row+Max_Nbr_Opts THEN { right-arrow/down-arrow keys }
  881.                         y := y + 1
  882.                       ELSE
  883.                         y := Row + 1
  884.                     ELSE
  885.                       IF Chk_Password(Password) THEN
  886.                         Option_Selected := TRUE;
  887.                   END;
  888.           Delim : BEGIN
  889.                     IF y < Row+Max_Nbr_Opts THEN { right-arrow/down-arrow keys }
  890.                       y := y + 1
  891.                     ELSE
  892.                       y := Row + 1;
  893.                   END;
  894.           Menu  : BEGIN { get the menu... }
  895.                     { de-select the option }
  896.                     QAttr (y,Col+1,1,Max_Opt_Len+4,fgMenu[Menu_Idx]+bgMenu[Menu_Idx]);
  897.  
  898.                     Temp_Menu_Ptr := Cur_Menu_Ptr;
  899.                     REPEAT
  900.                       IF Temp_Menu_Ptr^.Next_Menu_Ptr = NIL THEN
  901.                          Temp_Menu_Ptr := Head_Menu_Ptr
  902.                       ELSE
  903.                          Temp_Menu_Ptr := Temp_Menu_Ptr^.Next_Menu_Ptr;
  904.                     UNTIL (Temp_Menu_Ptr^.Menu_Name = Opt_Name) OR
  905.                           (Temp_Menu_Ptr = Cur_Menu_Ptr);
  906.                     IF Temp_Menu_Ptr^.Menu_Name = Opt_Name THEN { found the menu }
  907.                       IF LENGTH(Temp_Menu_Ptr^.Exit_Menu_Name) > 0 THEN { menu is already in use }
  908.                         Show_Error (7,8,8,fgNErr+bgNErr) { MENU ALREADY IN USE }
  909.                       ELSE { set the new menu }
  910.                         BEGIN
  911.                           IF Chk_Password(Password) THEN
  912.                             BEGIN
  913.                               Menu_Selected := TRUE;
  914.                               Cur_Opt_Row := y;
  915.                               IF Help_Toggle THEN
  916.                                 Erase_Help;
  917.                               Last_Menu_Nbr := Menu_Nbr;
  918.                               Menu_Nbr := Menu_Nbr + 1;
  919.                               Menu_Idx := Menu_Nbr MOD 16;
  920.                               Temp_Menu_Ptr^.Exit_Menu_Name := Menu_Name;
  921.                               Cur_Menu_Ptr := Temp_Menu_Ptr;
  922.                               Show_Menu (Cur_Menu_Ptr);
  923.                             END;
  924.                         END
  925.                     ELSE { have to add a new menu }
  926.                       IF Chk_Password(Password) THEN
  927.                         BEGIN
  928.                           Menu_Selected := TRUE;
  929.                           Cur_Opt_Row := y;
  930.                           IF Row + 2 <= CRTrows-(Max_Nbr_Opts+1) THEN
  931.                             y := Row + 2
  932.                           ELSE
  933.                             y := 3;
  934.                           IF Col + 6 < CRTcols-(Max_Opt_Len+8)+1 THEN
  935.                             x := Col + 6
  936.                           ELSE
  937.                             x := 6;
  938.                           Init_Menu (Temp_Menu_Ptr,Opt_Name,y,x);
  939.                           Changes := TRUE;
  940.                           Temp_Menu_Ptr^.Exit_Menu_Name := Menu_Name;
  941.                           Add_Menu (Temp_Menu_Ptr);
  942.                           IF Help_Toggle THEN
  943.                             Erase_Help;
  944.                           Last_Menu_Nbr := Menu_Nbr;
  945.                           Menu_Nbr := Menu_Nbr + 1;
  946.                           Menu_Idx := Menu_Nbr MOD 16;
  947.                           Cur_Menu_Ptr := Temp_Menu_Ptr;
  948.                           Show_Menu (Cur_Menu_Ptr);
  949.                         END;
  950.                   END;
  951.         END;
  952.     END;
  953.  
  954.     PROCEDURE Chk_Key (c : CHAR);
  955.     VAR i : BYTE;
  956.     BEGIN
  957.       WITH Cur_Menu_Ptr^ DO
  958.         BEGIN
  959.           i := y - Row;
  960.           REPEAT
  961.             i := i + 1;
  962.             IF i > Max_Nbr_Opts THEN
  963.               i := 1;
  964.           UNTIL (UpCase(Opts[i].Select_Key) = UpCase(c)) OR (i+Row = y);
  965.           IF UpCase(Opts[i].Select_Key) = UpCase(c) THEN
  966.             y := i + Row
  967.           ELSE
  968.             Show_Error (1,y,21,fgNErr+bgNErr); { UNSUPPORTED FUNCTION }
  969.         END;
  970.     END;
  971.  
  972.     FUNCTION Chk_Parms (Cur_Parms : STRING) : STRING;
  973.     VAR Reply : STRING;
  974.     BEGIN
  975.       Reply := Justify(Cur_Parms,Left,55,' ');
  976.       Chk_Parms := Reply;
  977.       IF Str_Question('New Parameters',CONCAT('Parameters ? [',Reply,']'),10,13,3,Reply,Visible_Chrs) THEN
  978.         Chk_Parms := Reply;
  979.     END;
  980.  
  981.     PROCEDURE Delete_Option (Cur_Row : BYTE);
  982.     BEGIN
  983.       WITH Cur_Menu_Ptr^ DO
  984.         WITH Opts[Cur_Row-Row] DO
  985.           IF LENGTH(Opt_Name) > 0 THEN
  986.             IF Chk_Password(Password) THEN
  987.               IF Opt_Question('Confirm Option Delete','Are you sure you want to delete this option',8,13,14) = 1 THEN
  988.                 BEGIN
  989.                   Changes := TRUE;
  990.                   Saved_Opt := Opts[Cur_Row-Row]; { save the current option }
  991.                   FILLCHAR (Opts[Cur_Row-Row],SIZEOF(Opts[Cur_Row-Row]),CHR(0)); { clear option }
  992.                   QWrite (Cur_Row,Col+1,-1,Make_String(Max_Opt_Len+4,' ')); { erase option from screen }
  993.                 END;
  994.     END;
  995.  
  996.     PROCEDURE Copy_Option (Opt_Nbr : BYTE);
  997.     BEGIN
  998.       WITH Cur_Menu_Ptr^.Opts[Opt_Nbr] DO
  999.         IF LENGTH(Opt_Name) > 0 THEN
  1000.           IF Chk_Password(Password) THEN
  1001.             Saved_Opt := Cur_Menu_Ptr^.Opts[Opt_Nbr];
  1002.     END;
  1003.  
  1004.     PROCEDURE Paste_Option (Cur_Row : BYTE);
  1005.     BEGIN
  1006.       IF LENGTH(Saved_Opt.Opt_Name) > 0 THEN
  1007.         WITH Cur_Menu_Ptr^ DO
  1008.           WITH Opts[Cur_Row-Row] DO
  1009.             IF (Opt_Type = Batch) AND
  1010.                ((Select_Key = CHR(0)) OR (Select_Key = ' ')) AND
  1011.                (LENGTH(Opt_Name) = 0) THEN
  1012.               BEGIN
  1013.                 Changes := TRUE;
  1014.                 Opts[Cur_Row-Row] := Saved_Opt;
  1015.                 QWrite (Cur_Row,Col+1,-1,CONCAT(Select_Key,'   ',Opt_Name)); { display the option }
  1016.               END
  1017.             ELSE
  1018.               Show_Error (6,9,9,fgNErr+bgNErr) { CANNOT PASTE OPTION }
  1019.       ELSE
  1020.         Sound_Bell;
  1021.     END;
  1022.  
  1023.     PROCEDURE Set_Bat_File (VAR Bat_File : TEXT; Opt : Opt_Rec; Menu_Nbr,Menu_Opt : BYTE);
  1024.     VAR Cur_Dir,Opt_Parms : STRING;
  1025.     BEGIN
  1026.       WITH Opt DO
  1027.         BEGIN
  1028.           IF LENGTH(Prg_Dir) > 0 THEN
  1029.             WRITELN (Bat_File,'CD ',Prg_Dir);
  1030.           IF LENGTH(Prg_Name) > 0 THEN
  1031.             BEGIN
  1032.               WRITE (Bat_File,Prg_Name);
  1033.               IF Prompts THEN
  1034.                 Opt_Parms := Chk_Parms(Parms)
  1035.               ELSE
  1036.                 Opt_Parms := Parms;
  1037.               IF LENGTH(Opt_Parms) > 0 THEN
  1038.                 WRITELN (Bat_File,' ',Opt_Parms)
  1039.               ELSE
  1040.                 WRITELN (Bat_File);
  1041.             END;
  1042.           IF Pause THEN
  1043.             BEGIN
  1044.               WRITELN (Bat_File,'ECHO ---READY TO RETURN TO MENUMGR---');
  1045.               WRITELN (Bat_File,'PAUSE');
  1046.             END;
  1047.           IF LENGTH(Prg_Name) > 0 THEN
  1048.             BEGIN
  1049.               GetDir (0,Cur_Dir);
  1050.               WRITELN (Bat_File,'CD ',Cur_Dir);
  1051.               WRITELN (Bat_File,'MM ',Menu_Nbr,' ',Menu_Opt);
  1052.             END;
  1053.         END;
  1054.     END;
  1055.  
  1056.   BEGIN { Manage_Menus }
  1057.     Cur_Menu_Ptr := Head_Menu_Ptr;
  1058.     IF Cur_Menu_Ptr = NIL THEN { special case - no menus }
  1059.       BEGIN
  1060.         Init_Menu (Cur_Menu_Ptr,'Main Menu',3,6);
  1061.         Changes := TRUE;
  1062.         Head_Menu_Ptr := Cur_Menu_Ptr;
  1063.       END;
  1064.     Abort := FALSE;
  1065.     Option_Selected := FALSE;
  1066.  
  1067.     { at this point, need to display all menus until the passed
  1068.       menu number and option are reached; will also need to reverse-build
  1069.       the Cur_Opt_Row values once final menu is reached; for now, just
  1070.       display the first menu only... }
  1071.  
  1072.     FILLCHAR(Saved_Opt,SIZEOF(Saved_Opt),CHR(0)); { blank out saved option }
  1073.     Menu_Nbr := 1;
  1074.     Last_Menu_Nbr := Menu_Nbr;
  1075.     Menu_Idx := Menu_Nbr MOD 16; { needed for attribute arrays }
  1076.     Show_Menu (Cur_Menu_Ptr);
  1077.     REPEAT
  1078.       WITH Cur_Menu_Ptr^ DO
  1079.         BEGIN
  1080.           IF Cur_Opt_Row = 0 THEN { start at first option }
  1081.             y := Row + 1
  1082.           ELSE { continue from where cursor was }
  1083.             y := Cur_Opt_Row;
  1084.           Old_y := y;
  1085.           QAttr (y,Col+1,1,Max_Opt_Len+4,fgHOpt+bgHOpt); { highlight first menu item }
  1086.           GotoRC (y,Col+Max_Opt_Len+5); { follow for appropriate placement of messages }
  1087.           Menu_Selected := FALSE;
  1088.           REPEAT
  1089.             IF Old_y <> y THEN
  1090.               BEGIN
  1091.                 QAttr (Old_y,Col+1,1,Max_Opt_Len+4,fgMenu[Menu_Idx]+bgMenu[Menu_Idx]);
  1092.                 Old_y := y;
  1093.               END;
  1094.             QAttr (y,Col+1,1,Max_Opt_Len+4,fgHOpt+bgHOpt);
  1095.             GotoRC (y,Col+40);
  1096.  
  1097.             REPEAT
  1098.               IF Date_Time_Toggle THEN
  1099.                 Show_Date_And_Time;
  1100.             UNTIL KeyPressed;
  1101.             Wait_For_Key (c,Extended);
  1102.             (* QAttr (y,Col+1,1,Max_Opt_Len+4,fgHOpt+bgHOpt+Blink); *)
  1103.             IF Extended THEN
  1104.               CASE ORD(c) OF
  1105.                  19 : Rename_Menu;                    { alt-r }
  1106.                  20 : Change_Time;                    { alt-t }
  1107.                  31 : Swap_Options (y);               { alt-s }
  1108.                  32 : Change_Date;                    { alt-d }
  1109.                  46 : Copy_Option (y-Row);            { alt-c }
  1110.                  47 : Paste_Option (y);               { alt-v }
  1111.                  F1 : Set_Help;                       { F1 - help toggle }
  1112.                  60 : Settings_Editor;                { F2 - options }
  1113.                  61 : Set_Exit (Cur_Menu_Ptr);        { F3 - exit }
  1114.                  F4 : Set_Header (Cur_Menu_Ptr);      { F4 - header toggle }
  1115.                  63 : Set_Date_Time;                  { F5 - date time toggle }
  1116.                  72,                                  { left-arrow }
  1117.                  75 : Up_Arrow (y);                   { up-arrow }
  1118.                  77,                                  { right-arrow }
  1119.                  80 : Down_Arrow (y);                 { down-arrow }
  1120.                  71 : y := Row + 1;                   { home }
  1121.                  79 : y := Row + Max_Nbr_Opts;        { end }
  1122.                  82 : Option_Editor (y-Row);          { insert }
  1123.                  45,                                  { alt-x }
  1124.                  83 : Delete_Option (y);              { delete }
  1125.                 115 : Move_Menu_Left (Cur_Menu_Ptr);  { ctrl-left-arrow }
  1126.                 116 : Move_Menu_Right (Cur_Menu_Ptr); { ctrl-right-arrow }
  1127.                 118 : Move_Menu_Down (Cur_Menu_Ptr);  { ctrl-pgdn }
  1128.                 132 : Move_Menu_Up (Cur_Menu_Ptr);    { ctrl-pgup }
  1129.                 ELSE { any other extended key }
  1130.                   Show_Error (1,10,21,fgNErr+bgNErr); { UNSUPPORTED FUNCTION }
  1131.               END
  1132.             ELSE
  1133.               CASE ORD(c) OF
  1134.                 13 : Chk_Enter (Cur_Menu_Ptr); { enter }
  1135.                 27 : Set_Exit (Cur_Menu_Ptr);  { escape }
  1136.                 ELSE { any other non-extended key }
  1137.                   Chk_Key (c);                 { select key pressed? }
  1138.               END;
  1139.           UNTIL Option_Selected OR Menu_Selected OR Abort;
  1140.           { at this point, a batch file is ready to go, we're quitting, or another menu has been selected }
  1141.         END;
  1142.     UNTIL Option_Selected OR Abort;
  1143.     { at this point, a batch file is ready to go, or we're quitting }
  1144.     { if option selected, see if there is a prompt for parameters... }
  1145.  
  1146.     ASSIGN (Batch_File,'GO.BAT');
  1147.     {$I-}
  1148.     REWRITE (Batch_File);
  1149.     {$I+}
  1150.     WRITELN (Batch_File,'ECHO OFF');
  1151.     WRITELN (Batch_File,'CLS');
  1152.     IF Option_Selected THEN
  1153.       WITH Cur_Menu_Ptr^.Opts[y-Cur_Menu_Ptr^.Row] DO
  1154.         IF LENGTH(Bat_Name) = 0 THEN { commands will be in "go" only }
  1155.           Set_Bat_File (Batch_File,Cur_Menu_Ptr^.Opts[y-Cur_Menu_Ptr^.Row],Menu_Nbr,y-Cur_Menu_Ptr^.Row)
  1156.         ELSE
  1157.           BEGIN
  1158.             ASSIGN (User_Batch_File,CONCAT(Bat_Name,'.BAT'));
  1159.             {$I-}
  1160.             RESET (User_Batch_File);
  1161.             {$I+}
  1162.             IO_Error := IORESULT;
  1163.             IF IO_Error = 2 THEN { create the user batch file }
  1164.               BEGIN
  1165.                 {$I-}
  1166.                 REWRITE (User_Batch_File);
  1167.                 {$I+}
  1168.                 IO_Error := IORESULT;
  1169.                 IF IO_Error = 0 THEN
  1170.                   BEGIN
  1171.                     WRITELN (User_Batch_File,'ECHO OFF');
  1172.                     Set_Bat_File (User_Batch_File,Cur_Menu_Ptr^.Opts[y-Cur_Menu_Ptr^.Row],Menu_Nbr,y-Cur_Menu_Ptr^.Row);
  1173.                   END
  1174.                 ELSE
  1175.                   IF IO_Error > 0 THEN { some error I can't handle }
  1176.                     BEGIN
  1177.                       Show_Error (IO_Error+100,10,21,fgNErr+bgNErr); { I/O ERROR }
  1178.                     END;
  1179.               END
  1180.             ELSE
  1181.               IF IO_Error > 0 THEN { some error I can't handle }
  1182.                 BEGIN
  1183.                   Show_Error (IO_Error+100,10,21,fgNErr+bgNErr); { I/O ERROR }
  1184.                 END;
  1185.             {$I-}
  1186.             CLOSE (User_Batch_File);
  1187.             {$I+}
  1188.             WRITELN (Batch_File,Bat_Name);
  1189.           END;
  1190.     CLOSE (Batch_File);
  1191.   END;
  1192.  
  1193.   PROCEDURE Update_Menu_File;
  1194.   VAR Menu_File : TEXT;
  1195.       Temp_Menu_Ptr : Menu_Ptr;
  1196.       i : BYTE;
  1197.  
  1198.     PROCEDURE Mark_Menu_Status (Menu_Name : Opt_Str);
  1199.     VAR i : BYTE;
  1200.         Temp_Menu_Ptr : Menu_Ptr;
  1201.     BEGIN
  1202.       Temp_Menu_Ptr := Head_Menu_Ptr;
  1203.       WHILE (Temp_Menu_Ptr^.Menu_Name <> Menu_Name) AND (Temp_Menu_Ptr <> NIL) DO
  1204.         Temp_Menu_Ptr := Temp_Menu_Ptr^.Next_Menu_Ptr;
  1205.       IF Temp_Menu_Ptr^.Menu_Name = Menu_Name THEN
  1206.         IF Temp_Menu_Ptr^.Menu_Status <> Used THEN
  1207.           BEGIN
  1208.             Temp_Menu_Ptr^.Menu_Status := Used;
  1209.             FOR i := 1 TO Max_Nbr_Opts DO
  1210.               WITH Temp_Menu_Ptr^.Opts[i] DO
  1211.                 IF (Opt_Type = Menu) AND (LENGTH(Opt_Name) > 0) THEN
  1212.                   Mark_Menu_Status(Opt_Name);
  1213.           END;
  1214.     END;
  1215.  
  1216.   BEGIN { Update_Menu_File }
  1217.     IF NOT Changes THEN { nothing to update }
  1218.       EXIT;
  1219.  
  1220.     { check menus in chain for in use }
  1221.     Mark_Menu_Status (Head_Menu_Ptr^.Menu_Name);
  1222.  
  1223.     { write out menufile }
  1224.     ASSIGN (Menu_File,'MENUFILE.DAT');
  1225.     {$I-}
  1226.     REWRITE (Menu_File);
  1227.     {$I+}
  1228.     IO_Error := IORESULT;
  1229.     IF IO_Error <> 0 THEN
  1230.       BEGIN
  1231.         Show_Error (IO_Error+100,10,21,fgNErr+bgNErr); { I/O ERROR }
  1232.         EXIT;
  1233.       END;
  1234.     Temp_Menu_Ptr := Head_Menu_Ptr;
  1235.     REPEAT
  1236.       WITH Temp_Menu_Ptr^ DO
  1237.         IF Menu_Status = Used THEN
  1238.           BEGIN
  1239.             WRITE (Menu_File,Menu_Name,Menu_Delim);
  1240.             WRITE (Menu_File,Row,Menu_Delim);
  1241.             WRITELN (Menu_File,Col);
  1242.             FOR i := 1 TO Max_Nbr_Opts DO
  1243.               WITH Opts[i] DO
  1244.                 BEGIN
  1245.                   CASE Opt_Type OF
  1246.                     Batch : WRITE (Menu_File,'BATCH',Opt_Delim);
  1247.                     Delim : WRITE (Menu_File,'DELIM',Opt_Delim);
  1248.                     Menu  : WRITE (Menu_File,'MENU',Opt_Delim);
  1249.                   END;
  1250.                   WRITE (Menu_File,Select_Key,Opt_Delim);
  1251.                   WRITE (Menu_File,Opt_Name,Opt_Delim);
  1252.                   WRITE (Menu_File,Prg_Dir,Opt_Delim);
  1253.                   WRITE (Menu_File,Prg_Name,Opt_Delim);
  1254.                   WRITE (Menu_File,Parms,Opt_Delim);
  1255.                   WRITE (Menu_File,Bat_Name,Opt_Delim);
  1256.                   WRITE (Menu_File,Password,Opt_Delim);
  1257.                   IF Pause THEN
  1258.                     WRITE (Menu_File,Pause,Opt_Delim)
  1259.                   ELSE
  1260.                     WRITE (Menu_File,Opt_Delim);
  1261.                   IF Prompts THEN
  1262.                     WRITELN (Menu_File,Prompts)
  1263.                   ELSE
  1264.                     WRITELN (Menu_File);
  1265.                 END;
  1266.           END;
  1267.       Temp_Menu_Ptr := Temp_Menu_Ptr^.Next_Menu_Ptr;
  1268.     UNTIL Temp_Menu_Ptr = NIL;
  1269.     {$I-}
  1270.     CLOSE (Menu_File);
  1271.     {$I+}
  1272.   END;
  1273.  
  1274. BEGIN { Menu_Manager }
  1275.   Init_Menu_Manager;         { initialize global attributes for hardware configuration }
  1276.   Bld_Menu_List;             { open menu file, build linked list, close file }
  1277.   ModCursor (CursorOff);     { turn the cursor off }
  1278.   Init_Scr (fgMain,bgMain);  { display the initial screen }
  1279.   Show_About_Msg;            { display the program identification }
  1280.   Manage_Menus;              { work with the menus }
  1281.   ModCursor (CursorOn);      { turn the cursor back on }
  1282.   Update_Menu_File;          { update menu file, if necessary }
  1283.   Update_Menu_Env;           { update menu enviroment, if necessary }
  1284. END.
  1285.