home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 9 / 09.iso / l / l042 / 1.ddi / COMMON.ARC / COMMON.PAS next >
Encoding:
Pascal/Delphi Source File  |  1987-12-30  |  32.4 KB  |  1,032 lines

  1. unit Common;
  2.  
  3. {----------------------------------------------------------------------------}
  4. {-                                                                          -}
  5. {-     Turbo Numerical Methods Toolbox                                      -}
  6. {-     Copyright (c) 1986, 87 Borland International, Inc.                   -}
  7. {-                                                                          -}
  8. {-     Contains I/O routines common to the entire toolbox.                  -}
  9. {-                                                                          -}
  10. {----------------------------------------------------------------------------}
  11.  
  12. {$I-}                  { Turn off I/O error checking }
  13.  
  14. {$I Float.inc}         { Determines the setting of $N compiler directive }
  15.  
  16. interface
  17.  
  18. uses
  19.   Dos, Crt;
  20.  
  21. var
  22.   OutFile : text;      { The standard output channel }
  23.   IOerr   : boolean;   { Flags I/O errors }
  24.  
  25. procedure DisplayWarning;
  26. { Send a warning message to OutFile. }
  27.  
  28. procedure DisplayError;
  29. { Send an error message to OutFile. }
  30.  
  31. procedure IOCheck;
  32. { Check for an I/O error and display an error message if needed. }
  33.  
  34. function InputChannel(Title : string) : char;
  35. { Displays a menu which allows the user to select either }
  36. { the keyboard or a file as a choice of where to get     }
  37. { input data from. If the keyboard is selected, 'K' is   }
  38. { returned otherwise, 'F' is returned.                   }
  39.  
  40. procedure GetOutputFile(var OutFile : text);
  41. { This procedure determines whether output should  }
  42. { be sent to the screen, printer, or a disk file.  }
  43. { The variable OutFile is returned as the standard }
  44. { output channel.                                  }
  45.  
  46. procedure ReadFloat(var FloatVar);
  47. { Returns a real number input from the user. If the user }
  48. { hits Return when being prompted for input, the default }
  49. { value assigned to FloatVar is returned. Editing is     }
  50. { allowed on all input.                                  }
  51.  
  52. procedure ReadInt(var IntVar : integer);
  53. { Returns an integer number input from the user. If the user }
  54. { hits Return when being prompted for input, the default     }
  55. { value assigned to IntVar is returned. Editing is allowed   }
  56. { on all input.                                              }
  57.  
  58. implementation
  59.  
  60. const
  61.   Null = #0;          { Ascii character codes }
  62.   Bell = #7;
  63.   Esc  = #27;
  64.   Cr   = #13;
  65.  
  66. type
  67.   String80 = string[80];  { Generic string type }
  68.  
  69. procedure DisplayWarning;
  70. begin
  71.   Writeln(OutFile, '               <* --------------------------- *>');
  72.   Write(OutFile, '               <*           ');
  73.   LowVideo;
  74.   Write(OutFile, 'WARNING           ');
  75.   HighVideo;
  76.   Writeln(OutFile, '*>');
  77.   Writeln(OutFile, '               <* --------------------------- *>');
  78.   Writeln(OutFile);
  79. end;  { procedure DisplayWarning }
  80.  
  81. procedure DisplayError;
  82. begin
  83.   Writeln(OutFile, '               !! --------------------------- !!');
  84.   Write(OutFile, '               !!            ');
  85.   LowVideo;
  86.   Write(OutFile, 'ERROR            ');
  87.   HighVideo;
  88.   Writeln(OutFile, '!!');
  89.   Writeln(OutFile, '               !! --------------------------- !!');
  90.   Writeln(OutFile);
  91. end;  { procedure DisplayError }
  92.  
  93. procedure Beep;
  94. begin
  95.   Write(Bell);
  96. end;
  97.  
  98. procedure IOCheck;
  99. var
  100.   IOcode : integer;
  101.  
  102. procedure Error(Msg : String80);
  103. begin
  104.   Writeln;
  105.   Beep;
  106.   Writeln(Msg);
  107.   Writeln;
  108. end; { procedure Error }
  109.  
  110. begin { procedure IOCheck }
  111.   IOcode := IOresult;
  112.   IOerr := IOcode <> 0;
  113.   if IOerr then
  114.     case IOcode of
  115.       2   : Error('File not found.');
  116.       3   : Error('Path not found.');
  117.       4   : Error('Too many open files.');
  118.       5   : Error('File access denied.');
  119.       6   : Error('Invalid file handle.');
  120.       12  : Error('Invalid file access code.');
  121.       15  : Error('Invalid drive number.');
  122.       16  : Error('Cannot remove current directory.');
  123.       17  : Error('Cannot rename across drives.');
  124.       100 : Error('Disk read error.');
  125.       101 : Error('Disk write error.');
  126.       102 : Error('File not assigned.');
  127.       103 : Error('File not open.');
  128.       104 : Error('File not open for input.');
  129.       105 : Error('File not open for output.');
  130.       106 : Error('Invalid numeric format.');
  131.       150 : Error('Disk is write-protected.');
  132.       151 : Error('Unknown unit.');
  133.       152 : Error('Drive not ready.');
  134.       153 : Error('Unknown command.');
  135.       154 : Error('CRC error in data.');
  136.       155 : Error('Bad drive request structure length.');
  137.       156 : Error('Disk seek error.');
  138.       157 : Error('Unknown media type.');
  139.       158 : Error('Sector not found.');
  140.       159 : Error('Printer out of paper.');
  141.       160 : Error('Device write fault.');
  142.       161 : Error('Device read fault.');
  143.       162 : Error('Hardware failure.');
  144.     else
  145.       begin
  146.         Writeln;
  147.         Writeln(Bell);
  148.         Writeln('Unidentified error message = ', IOcode, '. See manual.');
  149.         Writeln;
  150.       end;
  151.     end; { case }
  152. end; { procedure IOCheck }
  153.  
  154. {------------------------------------}
  155. {-                                  -}
  156. {-  Screen and cursor routines      -}
  157. {-                                  -}
  158. {------------------------------------}
  159.  
  160. const
  161.   FirstCol       = 1;    { The number of display columns }
  162.   LastCol        = 80;
  163.  
  164. type
  165.   CursorState    = (SaveCursor, RestoreCursor, OffCursor, BoxCursor, ULCursor);
  166.  
  167.   CursorRec      = record
  168.                      StartLine, EndLine : integer;
  169.                    end;
  170.  
  171.  
  172. const
  173.   OriginalCursor : CursorRec = (StartLine : -1; { init to illegal value }
  174.                                 EndLine   : -1);
  175.  
  176. var
  177.   BaseOfScreen   : word;         { The base address of screen memory }
  178.   WaitForRetrace : boolean;      { Flags video snow checking         }
  179.  
  180. procedure Cursor(WhichCursor : CursorState; var SavedCursor : CursorRec);
  181.  
  182. procedure SetCursor(StartLine, EndLine : integer);
  183. var
  184.   RegPack : Registers;
  185. begin
  186.   with RegPack do
  187.   begin
  188.     AX := $0100;  { cursor interrupt }
  189.     BX := $0;     { page #           }
  190.     CH := Lo(StartLine);
  191.     CL := Lo(EndLine);
  192.     Intr($10, RegPack);
  193.   end;
  194. end; { SetCursor }
  195.  
  196. procedure GetCursor(var StartLine, EndLine : integer);
  197. var
  198.   RegPack : Registers;
  199. begin
  200.   with RegPack do
  201.   begin
  202.     AX := $0300;  { cursor interrupt }
  203.     BX := $0;     { page #           }
  204.     Intr($10, RegPack);
  205.   end;
  206.   StartLine := Hi(RegPack.CX);
  207.   EndLine := Lo(RegPack.CX);
  208. end; { GetCursor }
  209.  
  210. begin { Cursor }
  211.   case WhichCursor of
  212.     SaveCursor : begin
  213.                    with SavedCursor do           { save previous cursor }
  214.                      GetCursor(StartLine, Endline);
  215.                  end;
  216.     RestoreCursor
  217.                : begin
  218.                    with SavedCursor do           { restore previous cursor }
  219.                      if (StartLine <> -1) and (EndLine <> -1) then
  220.                        SetCursor(StartLine, EndLine)
  221.                  end;
  222.     OffCursor : SetCursor(32, 0);
  223.     BoxCursor : SetCursor(1, 32);
  224.     ULCursor  : if BaseOfScreen = $B800 then         { color }
  225.                   SetCursor($06, $07)
  226.                 else
  227.                   SetCursor($0B, $0C);               { mono }
  228.   end; { case }
  229. end; { Cursor }
  230.  
  231. procedure GetScreenMode;
  232. var
  233.   RegPack   : Registers;
  234.   VideoMode : integer;
  235. begin
  236.   { Determine screen type for screen updating procedure }
  237.   RegPack.AX := $0F00;
  238.   { BIOS INT 10H call to get screen type }
  239.   Intr($10, RegPack);
  240.   VideoMode := RegPack.AL;
  241.   WaitForRetrace := VideoMode <> 7;
  242.   if WaitForRetrace then       { color? }
  243.     BaseOfScreen := $B800
  244.   else                         { mono }
  245.     BaseOfScreen := $B000;
  246.   Cursor(ULcursor, OriginalCursor);        { set UL cursor as default }
  247.   Cursor(SaveCursor, OriginalCursor);      { save it }
  248. end; { GetScreenMode }
  249.  
  250. var
  251.   MemAdr : word;      { Address in memory for next char to display }
  252.  
  253. procedure MoveFromScreen(var Source, Dest; Len : integer);
  254.  
  255. {  Move memory, as Turbo Move, but assume that the source is in
  256.    video memory; prevent screen flicker based on this assumption,
  257.    unless WaitForRetrace is false.  Timing is VERY tight: if the code
  258.    were 1 clock cycle slower, it would cause flicker. }
  259.  
  260. begin
  261.   if not WaitForRetrace then
  262.     Move(Source,Dest,Len)
  263.   else
  264.     begin
  265.       Len := Len Shr 1;
  266.       Inline($1E/$55/$BA/$DA/$03/$C5/$B6/ Source /$C4/$BE/ Dest /$8B/$8E/
  267.              Len /$FC/$EC/$D0/$D8/$72/$FB/$FA/$EC/$D0/$D8/$73/$FB/$AD/
  268.              $FB/$AB/$E2/$F0/$5D/$1F);
  269.     end;
  270. {
  271.         push    ds              ; Save Turbo's DS
  272.         push    bp              ;   and BP
  273.         mov     DX,3da          ; Point DX to CGA status port
  274.         lds     si,source[bp]   ; Source pointer into DS:SI
  275.         les     di,dest[bp]     ; Dest pointer into ES:DI
  276.         mov     CX,len[bp]      ; Length value into CX
  277.         cld                     ; Set string direction to forward
  278. .0:     in      al,DX           ; Get 6845 status
  279.         rcr     al,1            ; Check horizontal retrace
  280.         jb      .0              ; Loop if in horizontal retrace: this prevents
  281.                                 ;   starting in mid-retrace, since there is
  282.                                 ;   exactly enough time for 1 and only 1 LODSW
  283.                                 ;   during horizontal retrace
  284.         cli                     ; No ints during critical section
  285. .1:     in      al,DX           ; Get 6845 status
  286.         rcr     al,1            ; Check for horizontal retrace: LODSW is 1
  287.                                 ;   clock cycle slower than STOSW; because of
  288.                                 ;   this, the vertical retrace trick can't be
  289.                                 ;   used because it causes flicker!  (RCR AL,1
  290.                                 ;   is 1 cycle faster than AND AL,AH)
  291.         jnb     .1              ; Loop if not in retrace
  292.         lodsw                   ; Load the video word
  293.         sti                     ; Allow interrupts
  294.         stosw                   ; Store the video word
  295.         loop    .0              ; Go do next word
  296.         pop     bp              ; Restore Turbo's BP
  297.         pop     ds              ;   and DS
  298. }
  299. end; { MoveFromScreen }
  300.  
  301. procedure MoveToScreen(var Source, Dest; Len: integer);
  302.  
  303. {  Move memory, as Turbo Move, but assume that the target is in
  304.    video memory; prevent screen flicker based on this assumption,
  305.    unless RetraceMode is false.  Timing is VERY tight: if the code
  306.    were 1 clock cycle slower, it would cause flicker. }
  307.  
  308. begin
  309.   if not WaitForRetrace then
  310.     Move(Source,Dest,Len)
  311.   else
  312.     begin
  313.       Len := Len Shr 1;
  314.       Inline($1E/$55/$BA/$DA/$03/$C5/$B6/ Source /$C4/$BE/ Dest /$8B/$8E/
  315.              Len /$FC/$AD/$89/$C5/$B4/$09/$EC/$D0/$D8/$72/$FB/$FA/$EC/
  316.              $20/$E0/$74/$FB/$89/$E8/$AB/$FB/$E2/$EA/$5D/$1F);
  317.     end;
  318. {
  319.         push    ds              ; Save Turbo's DS
  320.         push    bp              ;   and BP
  321.         mov     DX,3da          ; Point DX to CGA status port
  322.         lds     si,source[bp]   ; Source pointer into DS:SI
  323.         les     di,dest[bp]     ; Dest pointer into ES:DI
  324.         mov     CX,len[bp]      ; Length value into CX
  325.         cld                     ; Set string direction to forward
  326. .0:     lodsw                   ; Grab a video word
  327.         mov     bp,AX           ; Save it in BP
  328.         mov     ah,9            ; Move horiz. + vertical retrace mask to fast
  329.                                 ;   storage
  330. .1:     in      al,DX           ; Get 6845 status
  331.         rcr     al,1            ; Check horizontal retrace
  332.         jb      .1              ; Loop if in horizontal retrace: this prevents
  333.                                 ;   starting in mid-retrace, since there is
  334.                                 ;   exactly enough time for 1 and only 1 STOSW
  335.                                 ;   during horizontal retrace
  336.         cli                     ; No ints during critical section
  337. .2:     in      al,DX           ; Get 6845 status
  338.         and     al,ah           ; Check for both kinds of retrace: IF the
  339.                                 ;   video board does not report horizontal
  340.                                 ;   retrace while in vertical retrace, this
  341.                                 ;   will allow several characters to be
  342.                                 ;   stuffed in during vertical retrace
  343.         jnz     .2              ; Loop if not equal zero
  344.         mov     AX,bp           ; Get the video word
  345.         stosw                   ; Store the video word
  346.         sti                     ; Allow interrupts
  347.         loop    .0              ; Go do next word
  348.         pop     bp              ; Restore Turbo's BP
  349.         pop     ds              ;   and DS
  350. }
  351. end; { MoveToScreen }
  352.  
  353. procedure SetMemAddress(Col, Row : byte);
  354.  
  355. { The global variable MemAdr is assigned the value of the next location
  356.   on the screen to be written to. }
  357.  
  358. begin
  359.   MemAdr := Pred(Row) * (2 * LastCol) +     { add in prev. rows }
  360.             (Pred(Col) * 2);                { add in Column offsets}
  361. end; { SetMemAddress }
  362.  
  363. procedure SaveScreen(Var Adr; Num : byte);
  364.  
  365. { Saves area of screen to temporary buffer.
  366.   The paramater Adr passed to this routine is used as a temporary buffer
  367.   to hold the next Num characters on the screen. }
  368.  
  369. begin
  370.   MoveFromScreen(Mem[BaseOfScreen:MemAdr], Adr, Num shl 1);
  371. end; { SaveScreen }
  372.  
  373. procedure RestoreScreen(var Adr; Num : byte);
  374.  
  375. { Restore the original contents of the screen.
  376.   The screen is restored with the contents of Adr. }
  377.  
  378. begin
  379.   MoveToScreen(Adr, Mem[BaseOfScreen:MemAdr], Num shl 1);
  380. end; { RestoreScreen }
  381.  
  382. procedure SaveWindow(var P; X1, Y1, X2, Y2 : integer);
  383.  
  384. { Fill buffer "P" with screen memory under window defined by parameters }
  385.  
  386. var
  387.   I : integer;
  388.   Width : integer;
  389.   Buffer : array[0..3999] of byte absolute P;
  390. begin;
  391.   Width := Succ(X2 - X1);
  392.   for I := Y1 to Y2 do
  393.   begin
  394.     SetMemAddress(X1, I);                       { current row, first col }
  395.     SaveScreen(Buffer[(I - Y1) * (Width * 2)], Width);
  396.   end;
  397. end;  { SaveWindow }
  398.  
  399. procedure RestoreWindow(var P; X1, Y1, X2, Y2 : integer);
  400.  
  401. { Restore screen memory window defined by parameters with contents of
  402.   buffer "P" }
  403.  
  404. var
  405.   I : integer;
  406.   Width : integer;
  407.   Buffer : array[0..3999] of byte absolute P;
  408. begin;
  409.   Width := Succ(X2 - X1);
  410.   for I := Y1 to Y2 do
  411.   begin
  412.     SetMemAddress(X1, I);     { current row, first col }
  413.     RestoreScreen(Buffer[(I - Y1) * (Width * 2)], Width);
  414.   end;
  415. end;  { RestoreScreen }
  416.  
  417. {------------------------------------}
  418. {-                                  -}
  419. {-  Menu routines                   -}
  420. {-                                  -}
  421. {------------------------------------}
  422.  
  423. const
  424.   ON  = true;    { Signals menu highlighting }
  425.   OFF = false;
  426.  
  427. type
  428.   { type for menu device selection }
  429.   OutputDevice    = (NoDevice, ScreenDevice, FileDevice, PrinterDevice);
  430.  
  431.   { types for save screen logic }
  432.   VideoRec        = record
  433.                       ASCIIchar : char;
  434.                       Att       : byte;
  435.                     end;
  436.  
  437.   VideoLineBuffer = array[1..LastCol] of VideoRec;
  438.  
  439. function GetWsKey : char;
  440. var
  441.   Ch : char;
  442. begin
  443.   Ch := ReadKey;
  444.   if (Ch = Null) and KeyPressed then
  445.   begin
  446.     Ch := ReadKey;
  447.     case Ch of
  448.       'H' : Ch := ^E;
  449.       'P' : Ch := ^X;
  450.     end;
  451.   end;
  452.   GetWsKey := UpCase(Ch);
  453. end; { GetWsKey }
  454.  
  455. type
  456.   BoxRec = record
  457.              UL, UR, LL, LR, Horiz, Vert, LT, RT, TT, BT : char;
  458.            end;
  459. const
  460.   { Used to store Ascii graphics charaters for drawing boxes }
  461.   SingleBox : BoxRec = (UL    : '┌'; UR    : '┐';
  462.                         LL    : '└'; LR    : '┘';
  463.                         Horiz : '─'; Vert  : '│';
  464.                         LT    : '├'; RT    : '┤';
  465.                         TT    : '┬'; BT    : '┴');
  466.  
  467. procedure DrawBox(X, Y, Width, Height : integer;
  468.                   Title : string80;
  469.                   BorderAtt, TitleAtt : integer);
  470. { This routine draws a box AROUND (outside) the window coordinates it is
  471.   given. It starts drawing a box at (x - 1, y - 1). The boxes dimensions
  472.   are be width + 2 wide and height + 2 high. }
  473. var
  474.   I        : integer;
  475.   S        : string[80];
  476.   SLen     : byte absolute S;
  477.   OldColor : integer;
  478. begin
  479.   Window(1, 1, 80, 25);
  480.   OldColor := TextAttr;
  481.   with SingleBox do
  482.   begin
  483.     FillChar(S, SizeOf(S), Horiz);  { fill string with horiz. chars }
  484.     SLen := Width;
  485.     X := Pred(X);
  486.     Y := Pred(Y);
  487.     Width := Succ(Width);
  488.     Height := Succ(Height);
  489.     TextAttr := BorderAtt;
  490.     GoToXY(X, Y);                               { upper left }
  491.     Write(UL, S, UR);
  492.     for I := 1 to Height do                     { sides }
  493.     begin
  494.       GoToXY(X, Y + I);
  495.       Write(Vert);
  496.       GoToXY(X + Width, Y + I);
  497.       Write(Vert);
  498.     end;
  499.     GoToXY(X, Y + Height);                      { lower left }
  500.     Write(LL, S, LR);
  501.  
  502.     { Center title on top of box }
  503.     if Title <> '' then
  504.     begin
  505.       GoToXY(X + Pred(Width - Ord(Title[0])) shr 1, Y);
  506.       TextAttr := TitleAtt;
  507.       Write(' ', Title, ' ');
  508.     end;
  509.   end; { with }
  510.   TextAttr := OldColor;
  511. end; { DrawBox }
  512.  
  513. procedure ShowMenuLine(S : String80;
  514.                    NumHi : integer;
  515.              HiAtt, X, Y : integer);
  516. { Write the string S at (X,Y). The first NumHi chars will be highlighted
  517.   using the HiAtt color. The remaining chars will be written in the current
  518.   color. }
  519. var
  520.   OldAtt : byte;
  521. begin
  522.   OldAtt := TextAttr;        { remember prev. attribute }
  523.   TextAttr := HiAtt;
  524.   GoToXY(X, Y);
  525.   Write(Copy(S, 1, NumHi));
  526.   TextAttr := OldAtt;        { restore }
  527.   Write(Copy(S, Succ(NumHi), 255));
  528. end; { ShowMenuLine }
  529.  
  530. var
  531.   PrevLineBuffer : VideoLineBuffer;
  532.  
  533. procedure ShowMenuBar(TurnOn : boolean;
  534.                         X, Y : integer;
  535.                     BarWidth : integer;
  536.                     BarColor : integer);
  537. { This routine reads the screen starting at (X, Y) and changes the
  538.   next BarWidth characters to the BarColor color. When the bar is turned
  539.   on, the current video line is preserved in the global PrevLineBuffer. When
  540.   the bar is turned off, the screen is restored from PrevLineBuffer). }
  541. var
  542.   I          : integer;
  543.   LineBuffer : VideoLineBuffer;
  544. begin
  545.   { calculate menu line's memory }
  546.   SetMemAddress(X, Y);
  547.   if TurnOn then
  548.     begin
  549.       SaveScreen(PrevLineBuffer, BarWidth);     { save curr. line from screen }
  550.       Move(PrevLineBuffer,                      { copy curr. line }
  551.            LineBuffer,
  552.            SizeOf(LineBuffer));
  553.       for I := 1 to BarWidth do                 { change attributes }
  554.         Linebuffer[I].Att := BarColor;
  555.       RestoreScreen(LineBuffer, BarWidth);      { write new line to screen }
  556.     end { if }
  557.   else
  558.     RestoreScreen(PrevLineBuffer, BarWidth);    { restore prev. line }
  559. end; { ShowMenuBar }
  560.  
  561. function UseMenu(X, Y, CurrItem, NumItems, BarWidth,
  562.                  MenuBarColor : integer; MenuChoices : String80) : char;
  563.  
  564. { Menu control routine: get a legal menu selection character }
  565.  
  566. var
  567.   CursorData : CursorRec;
  568.   P          : integer;
  569.   Ch         : char;
  570.  
  571. begin { UseMenu }
  572.   Cursor(SaveCursor, CursorData);            { save prev. cursor }
  573.   Cursor(OffCursor, CursorData);             { turn cursor off }
  574.   repeat
  575.     repeat
  576.       ShowMenuBar(ON,
  577.                   X, Y + Pred(CurrItem),
  578.                   BarWidth, MenuBarColor);
  579.       Ch := GetWsKey;                      { Get a menu command key }
  580.       ShowMenuBar(OFF,
  581.                   X, Y + Pred(CurrItem),
  582.                   BarWidth, MenuBarColor);
  583.  
  584.       { process keyboard input }
  585.       case Ch of
  586.         Esc, Cr : { Do nothing };
  587.  
  588.         ^E      : begin                         { up arrow }
  589.                     CurrItem := Pred(CurrItem);
  590.                     if CurrItem < 1 then
  591.                       CurrItem := NumItems;
  592.                   end;
  593.  
  594.         ^X      : begin                         { dn arrow }
  595.                     CurrItem := Succ(CurrItem);
  596.                     if CurrItem > NumItems then
  597.                       CurrItem := 1;
  598.                   end;
  599.       else                               { legal menu choice? }
  600.         P := Pos(Ch, MenuChoices);
  601.         if P = 0 then
  602.           Ch := Null
  603.         else
  604.           begin
  605.             CurrItem := P;     { move curr item to selected one }
  606.             Ch := Cr;          { simulate CR }
  607.           end;
  608.       end; { case }
  609.     until Ch <> Null;
  610.   until (Ch = Cr) or (Ch = Esc);
  611.  
  612.   { Done: return ordinal # (1, 2, 3..) or ESC }
  613.   if Ch = Cr then
  614.     UseMenu := Chr(CurrItem) { return ordinal number as a character #1, #2 etc. }
  615.   else
  616.     UseMenu := Ch;
  617.   Cursor(RestoreCursor, CursorData);         { restore cursor }
  618. end; { UseMenu }
  619.  
  620. type
  621.   { The type of menu requested }
  622.   MenuType = (InputSelection, OutPutSelection);
  623.  
  624. function PrintMenu(Title : String80; TypeOfMenu : MenuType) : OutputDevice;
  625. { Displays a menu for either input or output selection }
  626.  
  627. const
  628.   X         = 30;   { The upper left corner of the menu }
  629.   Y         = 10;
  630.   Height    = 5;    { The height of the menu }
  631.   HiAtt     = $0F;  { Character attributes for menu }
  632.   LoAtt     = $07;
  633.   BarAtt    = $70;
  634.   CurrItem  = 1;    { The default item that is highlighted }
  635.   BoxHeight = 7;    { The height of the menu box }
  636.   MaxBuffer = 40;   { Determines size of MenuBuf }
  637.  
  638. var
  639.   { Stores the screen beneath the menu }
  640.   MenuBuf    : array[1..MaxBuffer, 1..MaxBuffer] of VideoRec;
  641.   { Stores the screen beneath the help line }
  642.   HelpBuf    : array[1..80] of VideoRec;
  643.  
  644.   OldX, OldY : integer; { Old cursor position }
  645.   OldColor   : integer; { Old text color }
  646.   Ch         : char;    { Key hit by user }
  647.   NumItems   : integer; { # of menu items }
  648.   Width      : integer; { Width of a particular menu }
  649.   BoxWidth   : integer; { Width of Box around menu }
  650.  
  651. procedure ShowMenuHelpLine;
  652. { Display some help text on the 25th line of the screen }
  653.  
  654. const
  655.   KeyHelpRow  = 25;
  656.   KeyCapColor = $70;
  657.  
  658. begin
  659.   GoToXY(1, KeyHelpRow);
  660.   ClrEOL;
  661.   ShowMenuLine(^X'-', 1, KeyCapColor, 2, KeyHelpRow);
  662.   ShowMenuLine(^Y'-scroll', 1, KeyCapColor, WhereX, KeyHelpRow);
  663.   ShowMenuLine(^Q#217'-select', 2, KeyCapColor, WhereX + 2, KeyHelpRow);
  664.   ShowMenuLine('ESC-exit', 3, KeyCapColor, WhereX + 2, KeyHelpRow);
  665. end; { ShowMenuHelpLine }
  666.  
  667. procedure ShowMenuLines;
  668. begin
  669.   DrawBox(X, Y, Width, Height, Title, HiAtt, HiAtt);
  670.   Window(X, Y, X + Pred(Width), Y + Pred(height));
  671.   ClrScr;
  672.   if TypeOfMenu = InputSelection then
  673.     begin
  674.       ShowMenuLine('Keyboard',  1, HiAtt, 2, 2);
  675.       ShowMenuLine('File',    1, HiAtt, 2, 3);
  676.       NumItems := 2;
  677.     end
  678.   else
  679.     begin
  680.       ShowMenuLine('Screen',  1, HiAtt, 2, 2);
  681.       ShowMenuLine('File',    1, HiAtt, 2, 3);
  682.       ShowMenuLine('Printer', 1, HiAtt, 2, 4);
  683.       NumItems := 3;
  684.     end;
  685. end; { ShowMenuLines }
  686.  
  687. begin
  688.   Width := Length(Title) + 2;
  689.   if Width < 18 then
  690.     Width := 18;
  691.   BoxWidth := Width + 2;
  692.   GetScreenMode;
  693.   { Save old "environment" }
  694.   OldX := WhereX;
  695.   OldY := WhereY;
  696.   OldColor := TextAttr; { save color }
  697.   SaveWindow(MenuBuf, Pred(X), Pred(Y), X + BoxWidth - 1, Y + BoxHeight - 1);
  698.  
  699.   { Paint the menu }
  700.   TextAttr := LoAtt;
  701.   Window(1, 1, 80, 25);
  702.   SaveWindow(HelpBuf, 1, 25, 80, 25);
  703.   ShowMenuHelpLine;
  704.   ShowMenuLines;
  705.   if TypeOfMenu = OutputSelection then
  706.     { use the menu, return #1..#3 or ESC }
  707.     Ch := UseMenu(X, Y + 1, CurrItem, NumItems, Width, BarAtt, 'SFP')
  708.   else
  709.     { use the menu, return #1, #2 or ESC }
  710.     Ch := UseMenu(X, Y + 1, CurrItem, NumItems, Width, BarAtt, 'KF');
  711.   case Ch of
  712.     Esc : PrintMenu := NoDevice
  713.   else
  714.     PrintMenu := OutputDevice(Ord(Ch));
  715.   end;
  716.  
  717.   { Restore old "environment" }
  718.   Window(1, 1, 80, 25);
  719.   GoToXY(OldX, OldY);
  720.   TextAttr := OldColor;  { restore color }
  721.   RestoreWindow(MenuBuf, Pred(X), Pred(Y), X + BoxWidth - 1, Y + BoxHeight - 1);
  722.   RestoreWindow(HelpBuf, 1, 25, 80, 25);
  723. end; { PrintMenu }
  724.  
  725. {------------------------------------}
  726. {-                                  -}
  727. {-  I/O Selection routines          -}
  728. {-                                  -}
  729. {------------------------------------}
  730.  
  731. procedure Abort;
  732. begin
  733.   Window(1, 1, 80, 25);
  734.   NormVideo;
  735.   ClrEol;
  736.   GotoXY(1, 25);
  737.   Write('Program terminated by user.');
  738.   Halt;
  739. end; { Abort }
  740.  
  741. function InputChannel(Title : string) : char;
  742. begin
  743.   case PrintMenu(Title, InputSelection) of
  744.     ScreenDevice : InputChannel := 'K';
  745.     FileDevice   : InputChannel := 'F';
  746.     NoDevice     : Abort; { Halt the program! }
  747.   else
  748.     InputChannel := 'K';
  749.   end; { case }
  750. end; { InputChannel }
  751.  
  752. procedure GetOutputFile(var OutFile : text);
  753. var
  754.   FileName : String;
  755.   Ch       : char;
  756.  
  757. begin
  758.   case PrintMenu('Send Output To', OutPutSelection) of
  759.     ScreenDevice  : begin
  760.                       FileName  := 'CON';
  761.                       Assign(OutFile, FileName);
  762.                       Rewrite(OutFile);
  763.                     end;
  764.  
  765.     PrinterDevice : begin
  766.                       FileName := 'PRN';
  767.                       Assign(OutFile, FileName);
  768.                       Rewrite(OutFile);
  769.                     end;
  770.  
  771.     FileDevice    : begin
  772.                       repeat
  773.                         Ch := 'Y';
  774.                         Writeln;
  775.                         Write('Enter file name ');
  776.                         Readln(FileName);
  777.                         Assign(OutFile, FileName);
  778.                         Reset(OutFile);
  779.                         if IOresult = 0 then  { The file already exists. }
  780.                         begin
  781.                           Close(OutFile);
  782.                           Writeln;
  783.                           Write('This file already exists. ');
  784.                           Write('Write over it (Y/N)? ');
  785.                           Ch := UpCase(ReadKey);
  786.                           Writeln(Ch);
  787.                         end;
  788.                         if Ch = 'Y' then
  789.                         begin
  790.                           Rewrite(OutFile);
  791.                           IOCheck;
  792.                         end;
  793.                       until((Ch = 'Y') and not(IOerr));
  794.                     end;
  795.     NoDevice      : Abort; { Halt the program! }
  796.   else
  797.     FileName  := 'CON';
  798.     Assign(OutFile, FileName);
  799.     Rewrite(OutFile);
  800.   end; { case }
  801. end; { procedure GetOutputFile }
  802.  
  803. {------------------------------------}
  804. {-                                  -}
  805. {-  String input routines           -}
  806. {-                                  -}
  807. {------------------------------------}
  808.  
  809. const
  810.   BS       = #8;       { Ascii character return codes }
  811.   LF       = #10;
  812.   F1       = #187;
  813.   F2       = #188;
  814.   F3       = #189;
  815.   F4       = #190;
  816.   F5       = #191;
  817.   F6       = #192;
  818.   F7       = #193;
  819.   F8       = #194;
  820.   F9       = #195;
  821.   F10      = #196;
  822.   UpKey    = #200;
  823.   DownKey  = #208;
  824.   LeftKey  = #203;
  825.   RightKey = #205;
  826.   PgUpKey  = #201;
  827.   PgDnKey  = #209;
  828.   HomeKey  = #199;
  829.   EndKey   = #207;
  830.   InsKey   = #210;
  831.   DelKey   = #211;
  832.  
  833. type
  834.   CharSet = set of char;  { Character set type }
  835.  
  836. function ScanKey : char;
  837. { Scan for a key. Two charater return codes are }
  838. { returned as the second character + #128.      }
  839. var
  840.   Ch : Char;
  841. begin
  842.   Ch := ReadKey;
  843.   if Ch = Null then
  844.     Ch := Chr(Ord(ReadKey) + 128);
  845.   if Ch in [^C, Esc] then
  846.     Abort;
  847.   ScanKey := Ch;
  848. end; { ScanKey }
  849.  
  850. procedure InputStr(var S     : String;
  851.                        L,X,Y : Integer;
  852.                        LegalChars,
  853.                        Term  : CharSet;
  854.                    var TC    : Char    );
  855. { Input the string S with a maximum length of L at position (X, Y).  }
  856. { LegalChars contains all of the characters allowed for input. Term  }
  857. { contains all of the characters allowed for terminating input. TC   }
  858. { is the actual charater that terminated input.                      }
  859. var
  860.   P     : integer;
  861.   Ch    : char;
  862.   first : boolean;
  863.  
  864. begin
  865.   first := true;
  866.   GotoXY(X,Y); Write(S);
  867.   P := 0;
  868.   repeat
  869.     GotoXY(X + P,Y);
  870.     Ch := Upcase(ScanKey);
  871.     if not (Ch in Term) then
  872.       case Ch of
  873.         #32..#126 : if (P < L) and
  874.                        (ch in LegalChars) then
  875.                     begin
  876.                       if First then
  877.                       begin
  878.                         Write(' ':L);
  879.                         Delete(S,P + 1,L);
  880.                         GotoXY(X + P,Y);
  881.                       end;
  882.                       if Length(S) = L then
  883.                         Delete(S,L,1);
  884.                       P := succ(P);
  885.                       Insert(Ch,S,P);
  886.                       Write(Copy(S,P,L));
  887.                     end
  888.                     else Beep;
  889.         ^S, LeftKey : if P > 0 then
  890.                         P := pred(P)
  891.                       else Beep;
  892.         ^D, RightKey : if P < Length(S) then
  893.                          P := succ(P)
  894.                        else Beep;
  895.          ^A, HomeKey : P := 0;
  896.          ^F, EndKey  : P := Length(S);
  897.          ^G, DelKey  : if P < Length(S) then
  898.                        begin
  899.                          Delete(S,P + 1,1);
  900.                          Write(Copy(S,P + 1,L),' ');
  901.                        end;
  902.                  BS : if P > 0 then
  903.                  begin
  904.                    Delete(S,P,1);
  905.                    Write(^H,Copy(S,P,L),' ');
  906.                    P := pred(P);
  907.                  end
  908.                  else Beep;
  909.         ^Y : begin
  910.                Write(' ':L);
  911.                Delete(S,P + 1,L);
  912.              end;
  913.       else;
  914.     end;  {of case}
  915.     First := false;
  916.   until Ch in Term;
  917.   P := Length(S);
  918.   GotoXY(X + P,Y);
  919.   Write('' :L - P);
  920.   TC := Ch;
  921. end; { InputStr }
  922.  
  923. {------------------------------------}
  924. {-                                  -}
  925. {-  Numeric input routines          -}
  926. {-                                  -}
  927. {------------------------------------}
  928.  
  929. function StrToFloat(NumStr : string; var Num) : integer;
  930. { Converts a numeric string to either real or double  }
  931. { depending upon how the $N compiler directive is set }
  932. { A function result of zero indicates that no errors  }
  933. { occurred.                                           }
  934. var
  935.   Code : integer;
  936. {$IFOPT N+}
  937.   r    : double;
  938. {$ELSE}
  939.   r    : real;
  940. {$ENDIF}
  941.  
  942. begin
  943.   Val(NumStr, r, Code);
  944.   StrToFloat := Code;
  945.   if Code <> 0 then Exit;   { Invalid numeric string }
  946.   {$IFOPT N+}
  947.     double(Num) := r
  948.   {$ELSE}
  949.     real(Num) := r
  950.   {$ENDIF}
  951. end; { StrToFloat }
  952.  
  953. function StrToInt(NumStr : string; var Num : integer) : integer;
  954. { Coverts a numeric string to an integer.             }
  955. { A function result of zero indicates that no errors  }
  956. { occurred. -1 is returned if a range error occurred. }
  957. var
  958.   Code : integer;
  959.   l    : longint;
  960. begin
  961.   Val(NumStr, l, Code);
  962.   StrToInt := Code;
  963.   if Code <> 0 then Exit;      { Invalid numeric string }
  964.   if (l >= -32768) and (l <= 32767) then
  965.     Num := l
  966.   else
  967.     StrToInt := -1;  { Value out of legal integer range }
  968. end; { StrToInt }
  969.  
  970. const
  971.   Terminators : CharSet = [CR];  { Legal terminating character set }
  972.  
  973. procedure ReadFloat(var FloatVar);
  974. { Returns a real number input from the user. If the user }
  975. { hits Return when being prompted for input, the default }
  976. { value assigned to FloatVar is returned. Editing is     }
  977. { allowed on all input.                                  }
  978.  
  979. const
  980.   MaxLen = 25; { the maximum length of the input area }
  981. var
  982.   NumStr    : string;
  983. {$IFOPT N+}
  984.   TempFloat : double;
  985. {$ELSE}
  986.   TempFloat : real;
  987. {$ENDIF}
  988.   TC        : char;
  989.  
  990. begin
  991.   {$IFOPT N+}
  992.   Str(Double(FloatVar), NumStr);
  993.   {$ELSE}
  994.   Str(real(FloatVar), NumStr);
  995.   {$ENDIF}
  996.   InputStr(NumStr, MaxLen, WhereX, WhereY, ['0'..'9', '.', '-', '+', 'e', 'E'],
  997.              Terminators, TC);
  998.   if Length(NumStr) > 0 then
  999.     if StrToFloat(NumStr, TempFloat) = 0 then
  1000.       {$IFOPT N+}
  1001.       double(FloatVar) := TempFloat;
  1002.       {$ELSE}
  1003.       real(FloatVar) := TempFloat;
  1004.       {$ENDIF}
  1005. end; { ReadFloat }
  1006.  
  1007. procedure ReadInt(var IntVar : integer);
  1008. { Returns an integer number input from the user. If the user }
  1009. { hits Return when being prompted for input, the default     }
  1010. { value assigned to IntVar is returned. Editing is allowed   }
  1011. { on all input.                                              }
  1012.  
  1013. const
  1014.   MaxLen = 8;  { the maximum length of the input area }
  1015. var
  1016.   NumStr  : string;
  1017.   TempInt : integer;
  1018.   TC      : char;
  1019.  
  1020. begin
  1021.   Str(IntVar, NumStr);
  1022.   InputStr(NumStr, MaxLen, WhereX, WhereY, ['0'..'9', '+', '-'],
  1023.              Terminators, TC);
  1024.   if Length(NumStr) > 0 then
  1025.     if StrToInt(NumStr, TempInt) = 0 then
  1026.       IntVar := TempInt;
  1027. end; { ReadInt }
  1028.  
  1029. begin { Initialization section }
  1030.   IOerr := false;
  1031. end. { Common }
  1032.