peter пре 25 година
родитељ
комит
906b89300b
3 измењених фајлова са 1026 додато и 0 уклоњено
  1. 188 0
      packages/ncurses/db_demo.pp
  2. 448 0
      packages/ncurses/pxpic.inc
  3. 390 0
      packages/ncurses/pxpic.txt

+ 188 - 0
packages/ncurses/db_demo.pp

@@ -0,0 +1,188 @@
+{------------------------------------------------------------------------------
+                                 CncWare
+                           (c) Copyright 2000
+ ------------------------------------------------------------------------------
+  Filename..: db_demo.pp
+  Programmer: Ken J. Wright, [email protected]
+  Date......: 06/29/2000
+
+  Purpose - Demonstrate the use of oCrt in a simulated database record editor.
+
+-------------------------------<< REVISIONS >>---------------------------------
+  Ver  |    Date    | Prog| Description
+-------+------------+-----+----------------------------------------------------
+  1.00 | 06/29/2000 | kjw | Initial Release.
+-------------------------------------------------------------------------------
+}
+Program db_demo;
+Uses oCrt;
+Const
+   MAXCOLS = 6;
+   MAXROWS = 10;
+Type
+   tAddress = Record
+      FirstName,
+      LastName,
+      Street  : string[40];
+      Country : string[2];
+      Zip     : string[5];
+      City    : string[30];
+   End;
+
+   tFields = Record
+      x,y,wid : integer;
+      pic : string;
+   End;
+
+Var
+   win : tnWindow;
+   address : Array [1..MAXROWS] of tAddress;
+   fields : Array [1..MAXCOLS] of tFields;
+   s : string;
+   i,
+   m1,m2,
+   att1,att2,att3,
+   row,
+   col : integer;
+   ch : char;
+   IsDone : boolean;
+
+Procedure Display(row : integer);
+Begin
+   With address[row] Do Begin
+      For i := 1 to MAXCOLS Do Begin
+         With fields[i] Do Begin
+            Case i of
+               1 : s := FirstName;
+               2 : s := LastName;
+               3 : s := Street;
+               4 : s := Country;
+               5 : s := Zip;
+               6 : s := City;
+            End;
+            win.FWrite(x,y,att1,x+wid-1,s);
+         End;
+      End;
+   End;
+   col := 1;
+End;
+
+{ bind the arrow keys so they trigger an exit }
+Procedure BindArrows;
+Begin
+   win.ec.Special := ^I^R^L^P^N;
+   m1 := win.ec.AddChMap(#0+Char(nKeyRight)+^R#0);
+   m2 := win.ec.AddChMap(#0+Char(nKeyLeft)+^L#0);
+   win.FWrite(1,win.Rows,48,0,'[F2]-Arrows');
+End;
+
+Procedure UnBindArrows;
+Begin
+   win.ec.Special := ^R^L^P^N;
+   win.ec.ClrChMap(m1);
+   win.ec.ClrChMap(m2);
+   win.FWrite(1,win.Rows,62,0,'[F2]-Arrows');
+End;
+
+Begin
+   FillChar(address,SizeOf(address),#0);
+   With address[1] Do Begin
+      FirstName := 'Rainer';
+      LastName := 'Hantsch';
+      Street := '12345 Some Street';
+      Country := 'A';
+      Zip := '1030';
+      City := 'Vienna';
+   End;
+
+   For i := 1 to MAXCOLS Do Begin
+      With fields[i] Do Begin
+         Case i of
+            1 : Begin x := 14; y := 2; wid := 40; pic := ''; End;
+            2 : Begin x := 14; y := 3; wid := 40; pic := ''; End;
+            3 : Begin x := 14; y := 4; wid := 40; pic := ''; End;
+            4 : Begin x := 14; y := 5; wid :=  2; pic := ''; End;
+            5 : Begin x := 19; y := 5; wid :=  5; pic := '*#'; End;
+            6 : Begin x := 27; y := 5; wid := 30; pic := ''; End;
+         End;
+      End;
+   End;
+
+   att1 := 19; { field display color }
+   att2 := 31; { field edit color }
+   att3 := 23; { labels color }
+
+   nMakeWindow(win,1,1,60,10,att3,30,63,true,center,' Rainer''s Address Book ');
+   With win Do Begin
+      Align(center,center);
+      FWrite(1,Rows,48,Cols,'[F2]-Arrows [F10]-Exit [Tab]-NextField [^P]-Prev [^N]-Next');
+      Writeln;
+      Writeln(' First Name [                                        ]');
+      Writeln('  Last Name [                                        ]');
+      Writeln('     Street [                                        ]');
+      Write  ('   Zip/City [  ]-[     ] [                              ]');
+      Show;
+      ec.AddChMap(^P#0#0+Char(nKeyPgUp));
+      ec.AddChMap(^N#0#0+Char(nKeyPgDn));
+      BindArrows;
+      row := 1;
+      col := 1;
+      display(row);
+      IsDone := false;
+      Repeat
+         Str(row:2,s);
+         FWrite((cols-10) div 2,rows-1,26,0,'Record #'+s);
+         With address[row] Do Begin
+            With fields[col] Do Begin
+               ec.Picture := pic;
+               Case col of
+                  1 : s := FirstName;
+                  2 : s := LastName;
+                  3 : s := Street;
+                  4 : s := Country;
+                  5 : s := Zip;
+                  6 : s := City;
+               End;
+               s := Edit(x,y,att2,x+wid-1,x+Length(s),s,ch);
+               If ch <> #27 Then
+                  Case col of
+                     1 : FirstName := s;
+                     2 : LastName := s;
+                     3 : Street := s;
+                     4 : Country := s;
+                     5 : Zip := s;
+                     6 : City := s;
+                  End;
+               FWrite(x,y,att1,x+wid-1,s);
+               Case Ord(ch) of
+                   9,
+                  13,
+                  Ord(^r) : Inc(col);
+                  Ord(^l) : Dec(col);
+                  nKeyUp : Case col of
+                     1 : col := 4;
+                     2,3,4 : Dec(col);
+                     5,6 : col := 3;
+                  End;
+                  nKeyDown : Case col of
+                     1..3 : Inc(col);
+                     4..6 : col := 1;
+                  End;
+                  nKeyPgDn : Inc(row);
+                  nKeyPgUp : Dec(row);
+                  nKeyF2 : UnBindArrows; { use arrows for editing }
+                  nKeyF10 : IsDone := true;
+               End;
+            End;
+         End;
+         If row > MAXROWS Then row := MAXROWS;
+         If row < 1 Then row := 1;
+         If col > MAXCOLS Then col := 1;
+         If col < 1 Then col := MAXCOLS;
+         If Ord(ch) in [nKeyPgUp,nKeyPgDn] Then Display(row);
+         If Ord(ch) <> nKeyF2 Then BindArrows; { arrows for navigation }
+      Until IsDone;
+      Hide;
+      Done;
+   End;
+End.

+ 448 - 0
packages/ncurses/pxpic.inc

@@ -0,0 +1,448 @@
+{---------------------------------------------------------------------------
+                                 CncWare
+              Created and Copyright (c) 1991  J. John Sprenger
+----------------------------------------------------------------------------
+  Filename..: pxpic.inc
+  Programmer: Ken J. Wright, [email protected]
+  Date......: 06/09/2000
+
+  Purpose - Duplicates the functionality of the TPXPictureValidator.IsValid
+            method from Turbo Vision's validate unit. This function was
+            extracted from a unit called fmtline written by J. John Sprenger.
+            It was actually written before the validate unit was available
+            from Borland in TV2.0.
+
+-------------------------------<< REVISIONS >>--------------------------------
+  Ver  |   Date   | Prog| Description
+-------+----------+-----+-----------------------------------------------------
+  1.00 | 06/10/00 | kjw | Initial Release.
+  1.01 | 06/11/00 | kjw | Finally debugged the spin cycle! The AnyLeft function
+                        | missed a condition that left it an endless loop.
+                        | Added the boolean "done" to fix it.
+  1.02 | 06/15/00 | kjw | Added '@' to the match set.
+------------------------------------------------------------------------------}
+
+  {    Created and Copyright (c) 1991  J. John Sprenger    }
+
+  { tFormatLine.CheckPicture is the function that inspects }
+  { the input string passed as S against the Pic string    }
+  { which holds the Paradox-form Picture.  If an error is  }
+  { found the position of the error is placed in CPos.     }
+
+function nCheckPxPicture(var s, Pic : string;
+                      var CPos : integer) : word;
+  const
+    { flError, flCharOk and flFormatOK are constants used  }
+    { by tFormatLine.CheckPicture.  flError is returned    }
+    { when an error is found,  flCharOk when an character  }
+    { is found to be appropriate,  And flFormatOk when the }
+    { entire input string is found acceptable.             }
+    flError    = $0000;
+    flCharOK   = $0001;
+    flFormatOK = $0002;
+
+  var
+    Resolved  : integer;
+    TempIndex : integer;
+
+  { Function Copy represents a bit of syntactic sugar for  }
+  { the benefit of the author.  It changes the Copy func.  }
+  { so that its parameters represent start and end points  }
+  { rather than a start point followed by a quantity.      }
+  function Copy(s : string; start, stop : integer) : string;
+  begin
+    if stop < start then Copy:=''
+    else Copy:=System.Copy(s,start,stop-start+1);
+  end;
+
+  { Function FindMatch recursively locates the matching   }
+  (* grouping characters for "{" and "[".                *)
+  function FindMatch(P : string) : integer;
+  var
+    i:integer;
+    match:boolean;
+  begin
+    i:=2;
+    match:=false;
+    while (i<=length(P)) and not match do begin
+      if ((p[i]=']') and (p[1]='[')) or ((p[i]='}') and
+        (p[1]='{')) then
+        match:=true;
+      if p[i]='{' then
+        i:=i+FindMatch(Copy(p,i,length(p)))
+      else
+        if p[i]='[' then
+          i:=i+FindMatch(Copy(p,i,length(P)))
+        else inc(i);
+    end;
+    FindMatch:=i-1;
+  end;
+
+  { Function CP is the heart of tFormatLine.  It           }
+  { determines if the string, s, passed to it fits the     }
+  { requirements of the picture, Pic.  The number of       }
+  { characters successfully resolved is returned in the    }
+  { parameter resolved. When groups or repetitions are     }
+  { encountered CP will call itself recursively.           }
+  function CP(var s : string; Pic : string; var CPos :
+              integer; var Resolved : integer) : word;
+  const
+     CharMatchSet = ['#', '?', '&', '''', '@', '!'];
+  var
+    i          : integer;
+    index      : integer;
+    result_     : word;
+    commit     : boolean;
+    Groupcount : integer;
+
+  { Procedure Succeed resolves defaults and <Space>        }
+  { default requests                                       }
+
+  { Note:
+    The little patch below to exclude group end checking during
+    expansion lets autofill work as it should, however it also
+    autofills prematurely when there are more optionals or
+    alternates. I haven't quite figured how to make this work
+    correctly within the current recursion scheme.
+    kjw
+  }
+    procedure Succeed;
+    var
+      t     : integer;
+      found : boolean;
+    begin
+      if (i <= Length(s)) and
+         (s[i]=' ') and
+         (Pic[index]<>' ') and
+         (Pic[index]<>',')
+      then begin
+        t:=index;
+        found:=false;
+        while (t<=length(pic)) and not found do begin
+          if not (Pic[t] in (CharMatchSet+
+                 ['*','[','{',',',']','}'])) then begin
+            if pic[t]=';' then inc(t);
+            s[i]:=Pic[t];
+            found:=true;
+          end;
+          inc(t);
+        end;
+      end;
+      if (i>length(s)) then
+        {----------------------}
+        { Expand with defaults }
+        while not (Pic[index] in
+              (CharMatchSet+['*','[','{',',',']','}'])) and
+              (index<=length(Pic)) and
+              not(Pic[index-1] in [(*'}',*)','(*,']'*)]) do begin {kjw}
+          if Pic[index]=';' then inc(index);
+          s[i]:=Pic[index];
+          if i>length(s) then begin
+            CPos:=i;
+            s[0]:=char(i);
+          end;
+          inc(i);
+          inc(index);
+        end;
+    end;
+
+  { Function AnyLeft returns true if there are no required }
+  { characters left in the Picture string.                 }
+    function AnyLeft : boolean;
+    var
+       TempIndex : integer;
+       done : boolean; {kjw, 06/11/2000}
+    begin
+      done := false;
+      TempIndex:=index;
+      while ((Pic[TempIndex]='[') or (Pic[TempIndex]='*'))
+            and (TempIndex<=Length(Pic))
+            and (Pic[TempIndex]<>',')
+            and not done do begin
+        if Pic[TempIndex]='[' then
+          Tempindex:=Tempindex+FindMatch(Copy(Pic,index, Length(Pic)))
+        else begin
+          if not (Pic[TempIndex+1] in ['0'..'9']) then begin
+            inc(TempIndex);
+            if Pic[TempIndex] in ['{','['] then
+              tempIndex:=TempIndex+ FindMatch(Copy(pic,index,length(pic)))
+            else inc(TempIndex);
+          end else done := true;
+        end;
+      end;
+      AnyLeft:=(TempIndex<=length(Pic)) and
+               (Pic[TempIndex]<>',');
+    end;
+
+  { Function CharMatch determines if the current character }
+  { matches the corresponding character mask in the        }
+  { Picture string. Alters the character if necessary.     }
+    function CharMatch : word;
+    var result_ : word;
+    begin
+      result_:=flError;
+      case Pic[index] of
+        '#': if s[i] in ['0'..'9'] then result_:=flCharOk;
+        '?': if s[i] in ['A'..'Z','a'..'z'] then
+          result_:=flCharOk;
+        '&': if s[i] in ['A'..'Z','a'..'z'] then
+          begin
+            result_:=flCharOk;
+            s[i]:=upcase(s[i]);
+          end;
+       '''': result_:=flCharOk;
+        '@': result_:=flCharOk;
+        '!': begin
+           result_:=flCharOk;
+           s[i]:=upcase(s[i]);
+        end;
+      end;
+      if result_<>flError then commit:=true;
+      CharMatch:=result_;
+    end;
+
+  { Function Literal handles characters which are needed    }
+  { by the picture but otherwise used as format specifiers. }
+  { All such characters are preceded by the ';' in the      }
+  { picture string.                                         }
+    function Literal : word;
+    var result_ : word;
+    begin
+      inc(index);
+      if s[i]=Pic[index] then result_:=flCharOk
+      else result_:=flError;
+      if result_<>flError then commit:=true;
+      Literal:=result_;
+    end;
+
+  { Function Group handles required and optional groups    }
+  { in the picture string.  These are designated by the    }
+  (* "{","}" and "[","]" character pairs.                 *)
+    function Group:word;
+    var
+      result_: word;
+      TempS: string;
+      TempPic: string;
+      TempCPos: integer;
+      PicEnd: integer;
+      TempIndex: integer;
+      SwapIndex:integer;
+      SwapPic : string;
+    begin
+      TempPic:=Copy(Pic,index,length(Pic));
+      PicEnd:=FindMatch(TempPic);
+      TempPic:=Copy(TempPic,2,PicEnd-1);
+      TempS:=Copy(s,i,length(s));
+      TempCPos:=1;
+
+      result_:=CP(TempS,TempPic,TempCPos,TempIndex);
+
+      if result_=flCharOK then inc(GroupCount);
+      if (result_=flFormatOK) and (groupcount>0) then
+        dec(GroupCount);
+      if result_<>flError then result_:=flCharOk;
+
+      SwapIndex:=index;
+      index:=TempIndex;
+      SwapPic:=Pic;
+      Pic:=TempPic;
+      if not AnyLeft then result_:=flCharOk;
+      pic:=SwapPic;
+      index:=SwapIndex;
+      if i>1 then s:=copy(s,1,i-1)+TempS else s:=TempS;
+
+      CPos:=Cpos+TempCPos-1;
+      if Pic[index]='[' then begin
+        if result_<>flError then
+           i:=i+TempCPos-1
+        else dec(i);
+        result_:=flCharOK;
+      end
+      else i:=i+TempCPos-1;
+      index:=index+PicEnd-1;
+      Group:=result_;
+    end;
+
+  { Function Repetition handles characters that may be     }
+  { repeated in the input string.  The picture string      }
+  { indicates this possiblity with "*" character.          }
+    function Repetition:word;
+    var
+      result_:word;
+      count:integer;
+      TempPic:string;
+      TempS:string;
+      TempCPos:integer;
+      TempIndex:integer;
+      SwapIndex:integer;
+      SwapPic:string;
+      PicEnd:integer;
+      commit:boolean;
+
+      procedure MakeCount;
+      var nstr:string;
+          code:integer;
+      begin
+        if Pic[index] in ['0'..'9'] then begin
+          nstr:='';
+          repeat
+            nstr:=nstr+Pic[index];
+            inc(index);
+          until not(Pic[index] in ['0'..'9']);
+          val(nstr,count,code);
+        end
+        else count:=512;
+      end;
+
+      procedure MakePic;
+      begin
+        if Pic[index] in ['{','['] then begin
+          TempPic:=copy(Pic,index,length(Pic));
+          PicEnd:=FindMatch(TempPic);
+          TempPic:=Copy(TempPic,2,PicEnd-1);
+        end
+      else begin
+        if Pic[index]<>';' then begin
+          TempPic:=''+Pic[index];
+          PicEnd:=3;
+          if index=1 then
+            pic:='{'+pic[index]+'}'+ copy(pic,index+1,length(pic))
+          else pic:=copy(pic,1,index-1)+
+                         '{'+pic[index]+'}'+
+                         copy(pic,index+1,length(pic));
+        end
+        else begin
+          TempPic:=Pic[index]+Pic[index+1];
+          PicEnd:=4;
+          if index=1 then
+            pic:='{' + pic[index] + pic[index+1]+'}' +
+                 copy(pic,index+1,length(pic))
+          else pic:=copy(pic,1,index-1) + '{' + pic[index] +
+                    pic[index+1] + '}' + copy(pic,index+1,length(pic));
+        end;
+      end;
+    end;
+
+    begin
+      inc(index);
+      MakeCount;
+      MakePic;
+      result_:=flCharOk;
+      while (count<>0) and (result_<>flError) and
+            (i<=length(s)) do begin
+        commit:=false;
+        TempS:=Copy(s,i,length(s));
+        TempCPos:=1;
+        result_:=CP(TempS,TempPic,TempCPos,TempIndex);
+
+        if result_=flCharOK then inc(GroupCount);
+        if (result_=flFormatOK) and (groupcount > 0) then
+          dec(GroupCount);
+        if result_<>flError then result_:=flCharOk;
+
+        SwapIndex:=Index;
+        Index:=TempIndex;
+        SwapPic:=Pic;
+        Pic:=TempPic;
+        if (not AnyLeft) then result_:=flCharOk;
+        Pic:=SwapPic;
+        index:=SwapIndex;
+        if i>1 then s:=copy(s,1,i-1)+TempS else s:=TempS;
+        Cpos:=Cpos+TempCpos-1;
+        if (count>255) then begin
+          if result_<>flError then begin
+            i:=i+TempCpos-1;
+            if not commit then commit:=true;
+            result_:=flCharOk;
+          end
+          else dec(i);
+        end
+          else i:=i+TempCPos-1;
+        inc(i);
+        dec(count);
+      end;
+      dec(i);
+      index:=index+PicEnd-1;
+      if result_=flError then
+         if (count>255) and not commit
+           then result_:=flCharOk;
+      repetition:=result_;
+    end;
+
+    begin { of function CP}
+      i:=1;
+      index:=1;
+      result_:=flCharOk;
+      commit:=false;
+      Groupcount:=0;
+      while (i<=length(s)) and (result_<>flError) do begin
+        if index>length(Pic) then result_:=flError
+        else begin
+          if s[i]=' ' then Succeed;
+          if Pic[index] in CharMatchSet then
+            result_:=CharMatch
+          else
+            if Pic[index]=';' then
+              result_:=Literal
+            else
+              if (Pic[index]='{') or (Pic[index]='[') then
+                result_:=Group
+              else
+                if Pic[index]='*' then
+                  result_:=Repetition
+                else
+                  if Pic[index] in [',','}',']'] then
+                    result_:=flError
+                  else
+                    if Pic[index]=s[i] then begin
+                      result_:=flCharOk;
+                      commit:=true;
+                    end
+                    else result_:=flError;
+          if (result_ = flError) and not commit then begin
+            TempIndex:=Index;
+            while (TempIndex<=length(Pic)) and
+                  ((Pic[TempIndex]<>',') and
+                  (Pic[TempIndex-1]<>';'))  do begin
+              if (Pic[TempIndex]='{') or
+                 (Pic[TempIndex]=']') then
+                Index:=FindMatch(Copy( Pic,
+                                 TempIndex,length(Pic)))+TempIndex-1;
+                inc(TempIndex);
+            end;
+            if Pic[TempIndex]=',' then begin
+              if Pic[TempIndex-1]<>';' then begin
+                result_:=flCharOk;
+                index:=TempIndex;
+                inc(index);
+              end;
+            end;
+          end
+          else if result_<>flError then begin
+            inc(i);
+            inc(index);
+            Succeed;
+          end;
+
+        end;
+      end;
+      Resolved:=index;
+
+      if (result_=flCharOk) and
+         (GroupCount=0) and
+         (not AnyLeft or ((Pic[index-1]=',') and
+         (Pic[index-2]<>';'))) then
+         result_:=flFormatOk;
+
+      CPos:=i-1;
+      CP:=result_;
+    end;
+
+begin{ of function CheckPicture}
+   Resolved:=0;
+   CPos := 0;
+   If (Pic = '') or (s = '') Then
+      nCheckPxPicture := flFormatOk
+   Else
+      nCheckPxPicture:=CP(s,Pic,CPos,Resolved);
+end;

+ 390 - 0
packages/ncurses/pxpic.txt

@@ -0,0 +1,390 @@
+This text describes the Paradox picture input masking capabilities. It comes
+from chapter 5 of the Paradox PAL Programmers Guide for Paradox 4.0. This is
+what the nSEdit() function in the oCrt unit uses to mask user input when a
+picture string has been set for the nEC.Picture property.
+
+CHAPTER 5
+
+Using pictures to format input
+
+A PAL picture is a powerful and flexible tool for controlling what a
+user can type into a field during data entry. You can use pictures to
+
+a) shape and limit what the user can type into a field
+
+b) make data entry easier by filling in required or default values
+   automatically.
+
+You can think of pictures as a way to define new field types by
+imposing restrictions on existing ones. In effect, the Social Security
+number picture in Example 5-1 defines a new type of alphanumeric
+field. So would a picture of a telephone number (with or without the
+area code), or a part number in which X is always the second
+character. Pictures can also help users fill in default or repetitive
+values during data entry. For instance, through a picture, you can
+specify that the X in the part number will be filled in automatically.
+
+Example 5-1 Using a picture to format input.
+
+Suppose you want a user to enter Social Security numbers. Use a picture
+to make sure the input has the proper format.
+
+PICTURE = "###-##-###"
+
+The picture fills in the hyphens automatically and ensures that the user types
+the proper number of digits -- no other characters will be accepted.
+
+When a picture is specified, the user must fill it exactly and
+completely. In Example 5-1, if a user starts by typing a letter, a beep
+results. If someone tries to leave the field before typing all the digits,
+Paradox displays the message Incomplete field and leaves the cursor
+in the field.
+
+While a user is typing data into a picture, pressing Backspace or Del
+erases characters and Ctrl-Backspace clears the field as long as the
+resulting entry fills the picture. You can also design the picture to fill
+in optional characters when the user presses Space.
+
+How to define pictures
+
+A picture is a kind of pattern. It consists of a sequence of literal
+characters interlaced with any of the match characters listed in
+Table 5-1.
+
+Table 5-1  Match characters in pictures
+
+Picture element    Description
+----------------   --------------------------------------------
+
+Match characters
+
+#                  Accept only a digit
+?                  Accept only a letter (upper- or lowercase)
+&                  Accept only a letter, convert to uppercase
+@                  Accept any character
+!                  Accept any character, convert to uppercase
+t                  Any character taken literally
+
+Special characters
+
+;                  Take next character literally
+*                  Repetition count
+[]                 Option
+{}                 Grouping operators
+,                  Set of alternatives
+---------------------------------------------------------------
+Any number, letter, or punctuation character not defined as one of the unique
+match or special characters (that is, anything you can type that isn't on this
+list) is taken literally.
+
+The Social Security number in Example 5-1 was specified with the
+picture ###-##-####. The # is a match character that accepts only
+digits in its place. The hyphen is a literal character, meaning that it
+must literally appear in the typed value (matched exactly).
+Literal characters in a picture are filled in automatically unless
+
+a) you specify otherwise (see "Inhibiting automatic fill-in" later in
+   this chapter)
+b) they occur at the beginning of a picture (this helps to
+   accommodate blank fields)
+
+For example, when the cursor arrives at a blank field governed by the
+picture
+
+ABC-###
+
+the "ABC-" is not filled in automatically because it occurs at the
+beginning of the picture. But as soon as the user types A or a, or
+presses Space, the "ABC-" appears.
+
+If you want to specify a literal character that happens to be a match
+character, precede it with a semicolon (;). For example, here's how
+you'd specify a part number that contains three letters, a hyphen, and
+a number sign (#):
+
+???-;#
+
+If you omitted the semicolon, the picture would call for three letters,
+a hyphen, and a digit. You can use the semicolon in a picture to
+precede any of the characters in Table 5-1 that you want to be taken
+literallyincluding the semicolon itself. Here are some other
+examples:
+
+$*#.##;@      ; price each, like $345.67@
+*?;?          ; questions, adding ? to any text
+###;,###      ; six-digit number with comma separating thousands,
+              ; like 345.678
+???;;;#       ; part number with three letters, semicolon, and
+              ; number sign, like ABC;#
+
+
+Special features of
+pictures
+
+The special features of pictures are described in detail in the
+following sections. These spedal features are summarized in Table 5-2
+
+Table 5-2 Special features of pictures
+
+Operator  Name      Examples           Satisfied by
+--------  --------  -----------------  ---------------------------------------
+*         Repeat    *5#                Five numbers; equivalent to #####
+                    *#                 Zero or more numbers
+                    (*#.##), *#.##     Currency amounts, negative or
+                                       positive
+                    &*?                Initial capitalization of a single word
+{ }       Set       *5{##}             Ten numbers or five times the set of
+                                       two numbers
+
+                    {20,40,60,75,100}W Light bulbs in different wattages
+[ ]       Optional  ###-####[###[#]]   Phone number with or without a 3-
+                                       or 4-digit extension
+                    *[&[*?][@][ ]]     Any number of capitalized words
+                                       and initials
+,       Alternative RED,BLUE           Literals "RED" or "BLUE"; entering
+                                       an R fills RED, a B fills BLUE
+                    ###,##             2- or 3-digit number; ##,### would
+                                       not work
+{ }       Inhibit   ###{-)##           The hyphen is not filled in
+          Fill                         automatically: the user must press
+                                       Space to have the hvohen filled in
+------------------------------------------------------------------------------
+
+Repetition counts
+
+You'll find the repetition count match character (*) useful when
+specifying pictures for long fields. For example, these two pictures
+are equivalent:
+
+*25#
+#########################
+
+The match character * in the first picture is followed by a number,
+which is the number of times to repeat the match character or literal
+that follows. If you omit the *, the picture would mean the number 25
+followed by any other digit.
+
+To repeat a group of match characters, enclose the group in braces {}
+following the number of repetitions:
+
+*3{##:}   ; equivalent to ##:##:##:
+
+Notice how this picture mixes literal and match characters in the
+group. Each group consists of two digits to be typed by the user and
+a colon to be filled in by Paradox.
+
+Also use the grouping braces {} when you want to repeat a single
+number, as in
+
+*3{5}#   ; equivalent to 555# (three fives followed by any other digit)
+
+Without the braces, Paradox would think you wanted *35#, 35
+repetitions of "#".
+
+Omitting the number after the *, tells Paradox to accept zero or more
+repetitions of the character or group. You might use this variation
+when you want the user to type at least a certain number of
+characters, but perhaps more. For example, this picture in a State field
+
+&&*&
+
+requires at least the two-letter state abbreviation, but would also
+accept a state name spelled out in full.
+
+To use the * as a literal character in a picture, precede it with a
+semicolon (;). For example,
+
+;*###;*     ; equivalent to *###*
+
+Optional elements
+
+You can make part of a value optional by enclosing the
+corresponding part of the picture within brackets [ ]. This means that
+the user can enter data for that part of the value, but is not required
+to do so. The user can accept the optional part by typing a character
+that fits it or by pressing Space if the character is a literal.
+
+Note:  When the user reaches an optional part of a value, any matching
+character accepts the entire optional part. For example,
+
+[###l ###-####
+
+doesn't make the area code optional. Suppose the user tries to skip the
+area code and types the first digit of the phone number. Since this
+matches the first optional character, Paradox assumes it is the area
+code and requires all 10 digits.
+
+Similarly, the following picture won't work to specify a number with
+either two or three digits:
+
+[#]##
+
+since the first digit typed is always understood to be the [#]. To
+specify an optional number of digits, place the optional portion after
+the required portion:
+
+##[#]
+
+Here, the first two digits typed will match the mandatory #
+characters, and the third digit may or may not be added. Remember
+that pictures are always scanned left to right, so the optional portion
+of a repeating element should always come at the end.
+
+Example 5-2 demonstrates the correct way to create a picture for a
+telephone number with an optional area code.
+
+Example 5-2 Options in pictures
+
+The following picture accepts a 7-digit telephone number with an optional,
+parenthesized 3-digit area code:
+
+[(###)] ### - ####
+
+The user can fill in this field in two ways:
+
+a) If the first character typed is the left parenthesis or Space,
+   it matches the optional part of the picture and Paradox will look for a
+   3-digit area code before the 7-digit number.
+
+b) If the first character typed is a digit, Paradox ignores the option and
+   skips to the required digits. The right parenthesis (if used) and hyphen
+   are filled in automatically.
+
+Literal characters at the beginning of an optional part of a value are
+never filled in automatically. For example, in the picture
+
+#[ABC]#
+
+the characters "ABC" are not automatically filled in after the user
+types the first digit. They are filled in if the user types "A" or "a" or
+presses Space. The automatic fill-in occurs only when the filled-in
+characters are mandatory, or when the user explicitly elects the
+option by typing the first character or pressing Space.
+
+Optional elements can be nested, as in the following picture:
+
+#[[#[a]]
+
+Because the characters [ and ] are used to specify options, they must
+be preceded by a semicolon (;) if you want to use them literally in a
+picture.
+
+Alternative choices
+
+Many applications require the user to type one of several possible
+choices as part or all of a data value. A part number, for example,
+might consist of a three-digit number, followed by a color code of
+RED, GRE, YEL, or BLU, followed by another three-digit number.
+You specify a set of alternatives in a picture as a comma-separated list
+of alternatives. For example, you could specify the color-coded part
+numbers like this:
+
+###RED###, ###G RE###, ###YEL###, ###BLU###
+
+or, more succinctly,
+
+###{RED,GRE,YEL,BLU}###
+
+Both of these pictures have the same effect. In the second, the braces
+have been used to group the alternatives.
+
+As with bracketed options, literal characters at the beginning of an
+alternative are not filled in automatically. For example, if the user
+types 345 in the preceding example, the characters RED are not then
+filled in. But if the user then presses Spacebar or types r, the ED is
+filled in. Similarly, if the user types g, the RE is filled in, and so on.
+Paradox always fills in the first matching alternative it finds; thafs
+why RED is filled in when the user presses Space. To get one of the
+other options, the user must type its first letter.
+
+Example 5-3 Alternatives in a date picture
+
+The following example lets you restrict input in a date field to dates falling
+within the first week of a month:
+
+#[#]/{1.2,3,4.5,6,7}/##[#][#]
+
+Note the use of optional digits to permit months to have either one or two
+digits and years to have two, three, or four. Again, since pictures are scanned
+from left to right, the optional elements always come at the end of that part of
+the picture.
+
+The different alternatives need not be composed of literal characters;
+they can be picture specifications of any kind (Example 5-3). For
+example, here's another way of specifying a two- or three-digit
+number:
+
+###,##
+
+Again, the picture ##,### wouldn't do the job, because once the first
+two digits are typed, the first alternative will be selected and the
+picture will be fully satisfied. If the user then types a third digit, a
+beep sounds.
+
+The following picture specifies that an alphanumeric value can either
+be True or False:
+
+True,False
+
+Using this picture or variations on it, you can easily obtain the effect
+of a logical value or create a field of type logical in a Paradox table.
+
+As with other match characters, if you want to use a comma (,) as a
+literal character in a picture, you must precede it with a semicolon (;).
+
+Inhibiting automatic fill-in
+
+Some users find automatic fill-in of literal characters annoying
+particularly if they seldom look at the screen as they type. To inhibit
+it, simply enclose the literal characters you don't want filled in within
+braces I }. For example, this picture inhibits the fill-in of hyphens in
+Social Security numbers:
+
+###{-}##{-}####
+
+When the user types the first three digits, the hyphen (-) is not filled
+in. To enter it, the user can press - or Space.
+
+Sometimes you must inhibit fill-in to get the effect you want. In the
+color-coded part numbers in the previous section, suppose we had
+the color code BRO in addition to RED, GRE, YEL, and BLU. Adding
+BRO to the preceding picture gives
+
+###{RED,GRE,YEL,BLU,BRO}###
+
+Now suppose the user types three digits and then a b. Since BLU is
+the first possible match (left to right), Paradox would fill in the LU
+automatically. This could be wrong, since the user may have had
+BRO in mind rather than BLU. To solve this problem, use either of
+these pictures to inhibit automatic fill-in of the L:
+
+###{RED,GRE,YEL,B{L}U,BRO}###
+
+###{RED,GRE,YEL,B{LU,RO}}###
+
+The second picture, in effect, factors the letter B out of both BLU and
+BRO. This forces the user to type two characters to specify the blue or
+brown color code. The (LU, RO} is a set of alternative choices that has
+been nested within the larger set of choices.
+
+Going a step further, here's how you could inhibit all of the fill-in:
+
+###(R{E}{D},G{R}{E},Y{E}{L},B{L}{U},B{R}{0}}###
+
+Picture examples
+
+By using PAL's regular and special match characters, you can create
+an almost endless variety of picturesin effect, new data typesfor
+your applications. Here are just a few examples:
+
+(*#.##),*#.##             ; currency amounts, using parentheses for
+                          ; negative quantities
+#[#][#]*{;,###}           ; positive integers with commas separating
+                          ; groups of three digits
+{20,40,60,75,100}W        ; light bulbs in different wattages
+{A,B,C.D,E,F,G,H}[R]##-## ; tire sizes
+&*?                       ; initial capitalization of a single word
+&. &. &*?                 ; two initials followed by a capitalized name
+                          ; of any length
+*[&[*?][@][ ]]            ; any number of capitalized words or initials
+{##}:{##}:{##}            ; for time