home *** CD-ROM | disk | FTP | other *** search
/ Programmer 7500 / MAX_PROGRAMMERS.iso / PASCAL / XCRT.ZIP / XCRT.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1988-10-07  |  13.2 KB  |  502 lines

  1. Unit  xCRT;  {                     Version 1.2                      88/10/06
  2.  
  3. Useful extensions to the CRT unit.
  4.  
  5. This program is hereby donated to the public domain. It may be freely copied,
  6. used & modified without charge or fee.
  7.  
  8. Author        :  Mike Babulic
  9.                  3827 Charleswood Dr. N.W.
  10.                  Calgary, Alberta
  11.                  CANADA
  12.                  T2L 2C7
  13. Compuserve ID :  72307,314
  14.  
  15. }
  16.  
  17.  
  18. interface
  19.  
  20.   uses Dos
  21.  
  22. {$IFDEF VER40}
  23.    ,EnvUnit,Crt
  24. {$ENDIF};
  25.  
  26.  
  27. {--------------------------------------------------------------------------}
  28.  
  29. function GetKey: char;        {Waits for you to press a key on the keyboard}
  30.  
  31. var xKey : char;              {if GetKey returned 0, this contains the
  32.                                extended key code}
  33.  
  34. const                         {Useful characters & keyboard codes}
  35.     CR    = #13;
  36.     ESC   = #27;
  37.     BS    = #8;
  38.     TAB   = #9;
  39.  
  40.     xKeyCode = #0;    {extended key codes}
  41.  
  42.       NUL   = #3;
  43.  
  44.       Shift_TAB = #15;
  45.  
  46.       goHome  = #71;  goEnd   = #79;
  47.       Ins     = #82;  Del     = #83;
  48.  
  49.       goLeft   =  #75;   goRight  =  #77;  {arrow keys}
  50.       goUp     =  #72;   goDown   =  #75;
  51.  
  52.  
  53.  
  54. {--------------------------------------------------------------------------}
  55.  
  56.  
  57. function AskYN(prompt:string; defaultYes:boolean): boolean;
  58.   {prints the prompt and waits for a Y or N.
  59.    Returns TRUE if Y is pressed.
  60.     - returns defaultYes if CR is pressed}
  61.  
  62.  
  63. {--------------------------------------------------------------------------}
  64.  
  65. type
  66.     EfResult = (EfQuit   { Escape key pressed }
  67.                ,EfNormal { Normal Exit        }
  68.                ,EfGoNext { Normal Exit, go to next "enter field"}
  69.                ,EfGoPrev { Normal Exit, go to previous "enter field"}
  70.                );
  71.  
  72. function EnterField(
  73.                     var data{:string};   { Data string to be edited }
  74.                     MaxLen,              { Maximum length of data string }
  75.                     FieldLen,            { Length of edit field }
  76.                     Offset               { Starting cursor pos. in field }
  77.                       : integer
  78.            ):EfResult;
  79.   {Creates a 1 line data entry field at the current position on the screen.
  80.    Can use Ins,Del,Home,End and arrow keys to edit the field.
  81.    Escape is the Quit key.}
  82.  
  83. function MaxFieldLen(len:integer): integer;
  84.   {Maximum field length (up to "len") allowed at current cursor position}
  85.  
  86. procedure FrameField(MaxLen:integer);
  87.   {Build a Frame for a field at the current cursor position}
  88.  
  89. {--------------------------------------------------------------------------}
  90.  
  91.  
  92. type
  93.       XFName = record
  94.           d : DirStr;
  95.           n : NameStr;
  96.           x : ExtStr;
  97.           end;
  98.  
  99.       XFDlogKind = (forInput,forOutput);
  100.  
  101.  
  102. const  {Switches for GetXFName}
  103.  
  104.       XFSearchPath        :boolean = TRUE;  {Search the path for name}
  105.       XfAskAboutWildcards :boolean = TRUE;  {Warns user when wildcards in name}
  106.  
  107.  
  108. function GetXFName(prompt:string; defaultName:PathStr; kind:XFDlogKind;
  109.                    var name:XFName
  110.                   ):boolean;
  111.   {Use the EnterField to prompt the user for a file name.
  112.    Returns TRUE if "name" is a valid file name and the user selected it.
  113.      - prompt      : a message to prompt the user.
  114.      - defaultName : a initial value of "name".
  115.      - kind        : is the file going to be for input or output?
  116.      - name        : the file name is returned here.
  117.   }
  118.  
  119.  
  120. {--------------------------------------------------------------------------}
  121.  
  122.  
  123. function LoCase(c:char):char;  {returns lower case value of "c");
  124.  
  125.  
  126. procedure ClrLine;             {clear a line on the screen}
  127.  
  128.  
  129. function WindH : word;         {Horizontal size of current text window}
  130.  
  131. function WindV : word;         {Vertical size of current text window}
  132.  
  133.  
  134. {--------------------------------------------------------------------------}
  135.  
  136.  
  137. implementation
  138.  
  139. {$IFNDEF VER40}
  140.     uses EnvUnit,Crt;
  141. {$ENDIF}
  142.  
  143.   const
  144.     {special characters}
  145.  
  146.     cLeft    =  #27;   cRight   =  #26;  {visible arrow characters}
  147.     cUp      =  #24;   cDown    =  #25;
  148.  
  149. function LoCase(c:char):char;
  150.   begin
  151.     if c in ['A'..'Z'] then c := chr(ord(c)-ord('A')+ord('a'));
  152.     LoCase := c;
  153.   end;
  154.  
  155.  
  156. function GetKey: char;
  157.   var c:char;
  158.   begin
  159.     repeat until keypressed;
  160.     c := ReadKey;
  161.     if c=#0 then
  162.       xKey := ReadKey
  163.     else
  164.       xKey := #0;
  165.     GetKey := c;
  166.   end;
  167.  
  168. procedure ClrLine;
  169.   begin
  170.     gotoXY(1,whereY); ClrEol;
  171.   end;
  172.  
  173. function WindH : word;
  174.   begin
  175.     WindH := lo(WindMax) - lo(WindMin) + 1;
  176.   end;
  177.  
  178. function WindV : word;
  179.   begin
  180.     WindV := hi(WindMax) - hi(WindMin) + 1;
  181.   end;
  182.  
  183. function MaxFieldLen(len:integer): integer;
  184.   var max : integer;
  185.   begin
  186.     max := WindH-whereX+1;
  187.     if len<max then
  188.       MaxFieldLen := len
  189.     else begin
  190.       MaxFieldLen := max;
  191.       {Turbo's CRT unit will scroll when
  192.        you write in the bottom right corner, so..}
  193.          if WhereY=WindV then  MaxFieldLen := max-2;
  194.     end;
  195.   end;
  196.  
  197. procedure FrameField(MaxLen:integer);
  198.   var
  199.       x,y,i : integer;
  200.   begin
  201.     x := whereX;  y := whereY;
  202.     MaxLen := MaxFieldLen(MaxLen+3);
  203.     write('[');
  204.     for i := 3 to MaxLen do  write(' ');
  205.     write(' ]');
  206.     gotoXY(x+1,y);
  207.   end;
  208.  
  209.  
  210. function EnterField(
  211.                     var data{:string};   { Data string to be edited }
  212.                     MaxLen,              { Maximum length of data string }
  213.                     FieldLen,            { Length of edit field }
  214.                     Offset               { Starting cursor pos. in field }
  215.                       : integer
  216.            ):EfResult;
  217.   var
  218.       value : string  ABSOLUTE  data;
  219.       x,y : integer;
  220.       theLine: string;
  221.       len,i : integer;
  222.       key : char;
  223.       InsertMode : boolean;
  224.   procedure Update(first,last:integer);
  225.     forward;
  226.   function InRange(i:integer):integer;
  227.     begin
  228.       if      i<1      then  i:=1
  229.       else if i>MaxLen then  i:=MaxLen;
  230.       InRange := i;
  231.     end;
  232.   procedure go(pos:integer);
  233.     begin
  234.       pos := InRange(pos);
  235.       if not InsertMode then
  236.         update(i,pos);
  237.       i := pos;
  238.       if i < offset then begin
  239.         offset := i;
  240.         update(offset,offset+FieldLen-1);
  241.        end
  242.       else if i >= offset+FieldLen then begin
  243.         offset := i-FieldLen+1;
  244.         update(offset,offset+FieldLen-1);
  245.       end;
  246.       gotoXY(x+i-offset,y);
  247.     end;
  248.  
  249.   var UpdateRequest : record
  250.         Issued : Boolean;
  251.         first,last : integer;
  252.         end;
  253.   procedure Update(first,last:integer);
  254.     var t,j,k : integer;
  255.     begin
  256.       first := InRange(first);  last := InRange(last);
  257.       if first>last then begin
  258.         j := first;  first := last;  last := j;
  259.       end;
  260.       if UpdateRequest.Issued then begin
  261.         if first < UpdateRequest.First then
  262.           UpdateRequest.First  := first;
  263.         if last  > UpdateRequest.Last  then
  264.           UpdateRequest.Last   := last;
  265.        end
  266.       else begin
  267.         UpdateRequest.First  := first;
  268.         UpdateRequest.Last   := last;
  269.         UpdateRequest.Issued := TRUE;
  270.       end;
  271.     end;
  272.   procedure DoUpdate;
  273.     var oldCursor,toWrite,j : integer;
  274.         oldTextAttr : byte;
  275.     begin  with UpdateRequest do if Issued then begin
  276.       if first <  offset          then first := offset;
  277.       if last  >= offset+FieldLen then last  := offset+FieldLen-1;
  278.       oldCursor := i;
  279.       go(first);
  280.         write(copy(theLine,first,last-first+1));
  281.         toWrite := Length(theLine);
  282.         if toWrite<first then toWrite := first;
  283.         for j := toWrite to last do
  284.           write(' ');
  285.       go(oldCursor);
  286.       if not InsertMode then begin
  287.         oldTextAttr := TextAttr;
  288.         TextAttr := $7F AND NOT TextAttr;
  289.         if i<=len then
  290.           write(theLine[i])
  291.         else
  292.           write(' ');
  293.         TextAttr := oldTextAttr;
  294.         go(oldCursor);
  295.       end;
  296.       Issued := FALSE;
  297.     end  end;
  298.   procedure DelChar(pos:integer);
  299.     begin if (len>0) and (pos>0) and (pos<=len) then begin
  300.       delete(theLine,pos,1);
  301.       len := pred(len);
  302.       if len<i then go(len+1);
  303.       update(pos,len+1);
  304.     end  end;
  305.   procedure InsChar(pos:integer);
  306.     begin if (len<MaxLen) and (pos<=MaxLen)then begin
  307.       if pos<0   then  pos := 0;
  308.       if len>pos then begin
  309.         Move(theLine[pos],theLine[pos+1],len-pos+1);
  310.         len := succ(len);
  311.        end
  312.       else if len<pos then begin
  313.         FillChar(theLine[len+1],pos-len-1,' ');
  314.         len := pos;
  315.        end
  316.       else begin
  317.         len := succ(len);
  318.       end;
  319.       theLine[pos] := key;
  320.       theLine[0] := chr(len);
  321.       update(pos,len);
  322.     end  end;
  323.   procedure Echo;
  324.     begin
  325.       if (i>len) or InsertMode then
  326.         InsChar(i)
  327.       else begin
  328.         theLine[i] := key;
  329.         Update(i,i);
  330.        end;
  331.       go(succ(i));
  332.     end;
  333.   procedure FindEnd;
  334.     begin
  335.       while (len>0) and (theLine[len]=' ') do  len := pred(len);
  336.       theLine[0] := chr(len);
  337.       if len<offset then
  338.         go(1);
  339.       go(len+1);
  340.     end;
  341.   function EoEf : boolean;
  342.     begin
  343.       EoEf := (key IN [CR,ESC,TAB]) or (xKey = shift_TAB) or (i>=MaxLen);
  344.     end;
  345.   begin
  346.     UpdateRequest.Issued := FALSE;
  347.     x := whereX; y := whereY;
  348.     InsertMode := TRUE;
  349.     FieldLen := MaxFieldLen(FieldLen);
  350.     theLine := value;
  351.     len := length(value);
  352.     go(offset);
  353.     Update(1,len);
  354.     DoUpdate;
  355.     key := GetKey;
  356.     while not EoEf do begin
  357.         if key IN [' '..'~'] then
  358.           Echo
  359.         else if key = BS then begin
  360.           if i>1 then begin
  361.            go(pred(i));  DelChar(i);
  362.           end end
  363.         else if key = xKeyCode then
  364.           case xKey of
  365.             goLeft : go(pred(i));
  366.             goRight: go(succ(i));
  367.             goHome : go(1);
  368.             goEnd  : FindEnd;
  369.             Ins    : begin InsertMode := not InsertMode; update(i,i) end;
  370.             Del    : DelChar(i);
  371.           end; {case}
  372.         DoUpdate;
  373.         key := GetKey;
  374.     end;
  375.     if not InsertMode then begin
  376.       InsertMode := True;
  377.       Update(i,i);
  378.     end;
  379.     if key<> ESC then
  380.       value := theLine;
  381.     case key of
  382.       ESC :begin
  383.              EnterField := EfQuit;
  384.              theLine    := value;   len := length(theLine);
  385.              offset := 1;
  386.              update(1,MaxLen);
  387.            end;
  388.       CR  :  EnterField := EfNormal;
  389.       TAB :  EnterField := EfGoNext;
  390.     else
  391.       if xKey = Shift_TAB then  EnterField := EfGoPrev;
  392.     end;
  393.     doUpdate;
  394.   end;
  395.  
  396. function AskYN(prompt:string; defaultYes:boolean): boolean;
  397.   const
  398.       Yes = 'Y';
  399.       No  = 'N';
  400.   var
  401.       x,y,i  : integer;
  402.       answer : char;
  403.   begin
  404.     x := whereX;
  405.     y := whereY;
  406.     write(prompt,' [');
  407.       if defaultYes then
  408.         write(UpCase(Yes),',',LoCase(No),']',BS,BS)
  409.       else
  410.         write(LoCase(Yes),',',UpCase(No),']');
  411.       write(BS,BS);
  412.     repeat
  413.         answer := UpCase(GetKey);
  414.     until answer IN [Yes,No,CR];
  415.     gotoXY(x,y);
  416.       for i := 1 to length(prompt) do  write(' ');
  417.       write('      ');
  418.       gotoXY(x,y);
  419.     if answer = CR then
  420.       if defaultYes then answer := Yes else answer := No;
  421.     AskYN := (answer = Yes);
  422.   end;
  423.  
  424.  
  425. function GetXFName(prompt:string; defaultName:PathStr; kind:XFDlogKind;
  426.                    var name:XFName
  427.                   ):boolean;
  428.   const
  429.       FieldLen = 30;
  430.   var
  431.       k : char;
  432.       s,t : PathStr;
  433.       x,y : integer;
  434.       done,found : boolean;
  435.       flag : EfResult;
  436.   procedure ErrMsg(m:string);
  437.     begin
  438.       gotoXY(x,y); writeln;
  439.       write('     ',m);
  440.     end;
  441.   begin
  442.     found := FALSE;
  443.     x := whereX;
  444.     writeln;writeln;
  445.       y := whereY-2;
  446.     s := DefaultName;
  447.     repeat
  448.       gotoXY(x,y);
  449.       write(prompt);
  450.         ClrEol;
  451.       {get the answer}
  452.       FrameField(FieldLen);
  453.       done := (EfQuit = EnterField(s,SizeOf(s),FieldLen,1));
  454.       if not done then begin
  455.         if XFSearchPath then begin
  456.           t := FFind(s);
  457.           found := (t<>FFindErr);
  458.          end
  459.         else begin
  460.           found := fileExists(s);
  461.           if found then t := s;
  462.         end;
  463.       end;
  464.       if kind = forInput then begin
  465.         if not (found or done) then ErrMsg('File not found!');
  466.        end
  467.       else {forOutput} begin
  468.         if found then begin
  469.           ErrMsg('Overwrite "'+t+'"?');
  470.           found := AskYN('',false);
  471.           ClrLine;
  472.           if not (found or FileExists(s)) then begin
  473.             t := FExpand(s);
  474.             ErrMsg('Write to "'+t+'"?');
  475.             found := AskYN('',false);
  476.             ClrLine;
  477.           end;
  478.          end
  479.         else if not done then begin
  480.           t := s;
  481.           found := TRUE; {because we aren't overwriting anything}
  482.         end;
  483.       end;
  484.       if found and XfAskAboutWildcards and ContainsWildcards(s) then begin
  485.         ErrMsg('Filename contains wildcard characters.');
  486.         found := AskYN(' OK?',false);
  487.         ClrLine;
  488.       end;
  489.       done := done or found;
  490.     until done;
  491.     gotoxy(x,y+1); ClrEol;
  492.     gotoxy(x,y+2); ClrEol;
  493.     if found then begin
  494.       with name do  fsplit(FExpand(t),d,n,x);
  495.     end;
  496.     GetXFName := found;
  497.   end;
  498.  
  499.  
  500. begin
  501.   XFSearchPath := TRUE;
  502. end.