home *** CD-ROM | disk | FTP | other *** search
/ Piper's Pit BBS/FTP: ibm 0000 - 0009 / ibm0000-0009 / ibm0003.tar / ibm0003 / TPOWER54.ZIP / TPENTRY.ARC / TPENTRY.DOC < prev   
Encoding:
Text File  |  1989-07-10  |  48.5 KB  |  963 lines

  1.  
  2.                    Supplement to documentation for TPENTRY
  3.                    ---------------------------------------
  4.  
  5. How TPENTRY Works
  6. -----------------
  7.  
  8. Because TPENTRY has become increasingly complex over time, we think it might
  9. now be appropriate to give you a broad overview of how the whole data entry
  10. system works. It is our hope that, given a general understanding of how
  11. control flows from one part of the system to another, you will be better able
  12. to understand not only how to use the system, but also how to take advantage
  13. of the numerous hooks it makes available to the programmer.
  14.  
  15. From the broadest view, control flows like this:
  16.  
  17.     +----- 1 -----+
  18.     |   program   |
  19.     +-------------+
  20.        |      ^
  21.        v      |
  22.     +----- 2 -----+
  23.     |  EditScreen |
  24.     +-------------+
  25.        |      ^
  26.        v      |
  27.     +----- 3 -----+
  28.     |    field    |
  29.     |   editors   |
  30.     +-------------+
  31.  
  32. That is, the main program calls EditScreen to handle the editing of all the
  33. fields in the entry screen. It in turn calls upon one of the "field editors"
  34. (see p. 3-11) to process the individual fields. When the field editor returns
  35. control, EditScreen will either move the cursor to another field and continue
  36. editing, or return control to the main program. Depending on the "exit
  37. command" (see p. 3-5) issued by the user, the program may either terminate the
  38. editing session or make another call to EditScreen. (For an example of how to
  39. process the various exit commands, see ENTRY.PAS.)
  40.  
  41. Sharpening our focus a bit, we can see that the flow of control within
  42. EditScreen itself looks something like this:
  43.  
  44.     +----------- 1 -----------+
  45.     |  redraw entire screen   |
  46.     +-------------------------+
  47.         +----------- 2 -----------+
  48.         |     position cursor     | <----------+
  49.         +-------------------------+            |
  50.         +----------- 3 -----------+            |
  51.         |    convert to string    |            |
  52.         +-------------------------+            |
  53.             +----------- 4 -----------+        |
  54.             |  call pre-edit routine  |<---+   |
  55.             +-------------------------+    |   |
  56.             +----------- 5 -----------+    |   |
  57.             |    call field editor    |    |   |
  58.             +-------------------------+    |   |
  59.             +----------- 6 -----------+    |   |
  60.             |   convert from string   |    |   |
  61.             +-------------------------+    |   |
  62.             +----------- 7 -----------+    |   |
  63.             | call post-edit routine  |    |   |
  64.             +-------------------------+    |   |
  65.             +----------- 8 -----------+    |   |
  66.             |     process command     |    |   |
  67.             +-------------------------+    |   |
  68.                 |        |       |         |   |
  69.                exit     new     same       |   |
  70.              command   field    field      |   |
  71.                 |        |       +---------+   |
  72.                 v        +---------------------+
  73.  
  74. On entry to EditScreen, the entire screen is drawn (box 1). (We'll skip the
  75. details of how that's done because you don't really need to know.) After that,
  76. control flows into the larger of the routine's two main loops.
  77.  
  78. At the beginning of this loop (comprising boxes 2-8), the cursor is moved into
  79. the current field, then the "conversion routine" for that field is called (see
  80. p. 3-22). Its job in this situation is to convert the variable associated with
  81. the current field from its native type (integer, longint, real, date, etc.)
  82. into a string, then merge that string with the field's "picture mask" (see p.
  83. 3-6). This will allow the field editor to perform its duties without any real
  84. knowledge of the field's type.
  85.  
  86. Digressing for a moment: For each data type supported by TPENTRY there are
  87. three corresponding routines: one to add a field of that type to the entry
  88. screen, one to perform conversions, and one to perform validation. This
  89. "modular" approach offers two distinct advantages: Turbo Pascal's smart linker
  90. can strip out all the code that handles an unused data type, and the
  91. programmer can expand TPENTRY to handle new data types simply by writing the
  92. necessary conversion and validation routines. (See p. 3-22, "User-defined
  93. Types".) Now back to our flow charts...
  94.  
  95. Control then flows into the innermost loop (boxes 4-8). The first task here is
  96. to call the "pre-edit routine" if there is one (see p. 3-19). This gives the
  97. calling program a chance to display a help message if desired. The next step
  98. is to call one of the three field editors: the "string editor," the "numeric
  99. editor," or the "multiple-choice editor" (again, see p. 3-11). We'll discuss
  100. the job of the field editors in more detail below. When the field editor
  101. returns control to EditScreen, the conversion routine is called again, this
  102. time to convert the string being edited back to its native type. This process
  103. requires two steps: one to strip out the picture mask characters, and a second
  104. to convert the raw string into the appropriate type. (For examples of
  105. conversion routines, see TPENTRY.IN1.)
  106.  
  107. The next step is to call the "post-edit routine" if there is one (see p.
  108. 3-16). This is unquestionably the most important hook provided to the
  109. programmer by TPENTRY, and understanding it is the key to mastering the power
  110. of the system. You can do many things in a post-edit routine: update a
  111. calculated field; protect or unprotect a field; hide or unhide a field; change
  112. the command entered by the user; force the cursor to move to a particular
  113. field; the list goes on. The more common possibilities are discussed either in
  114. the manual or here in TPENTRY.DOC, and most of them are demonstrated in
  115. ENTRY.PAS. But the possibilities are almost unlimited, as some of our existing
  116. customers have proven.
  117.  
  118. After the post-edit routine has been called, EditScreen is ready to evaluate
  119. the command that caused the field editor to return control. This command will
  120. usually fall into one of two categories: an "exit command" (such as <Esc> or
  121. <CtrlEnter>) or a cursor movement command (such as <Enter>, <Up>, or <Down>).
  122. If it is an exit command, EditScreen will return control to the calling
  123. program, passing back the code for the exit command as its function result. If
  124. it is a cursor movement command, EditScreen has to calculate the field to move
  125. to. In some cases, the cursor may not need to be moved at all--for example, if
  126. the current "wrap mode" (see p. 3-73) is StopAtEdges, and the user has tried
  127. to move the cursor beyond the last row by pressing <Down>--and control flows
  128. back to the top of the inner loop (box 4). In most cases, however, the cursor
  129. will be moved to another field, and control will flow back to the top of the
  130. outer loop (box 2).
  131.  
  132. Digressing again for a moment: The category "exit commands" itself comprises
  133. two types of commands, pre-defined commands and user-defined commands.
  134. Creating a user-defined exit command is a simple two-step process: first you
  135. call AddEntryCommand to assign your command to a key (see Appendix C, under
  136. "Installable Keyboard Hooks"), then you write the code to process the command.
  137. For some good examples of how to use this hook, see ENTRY.PAS.
  138.  
  139. That pretty much covers the work done by EditScreen, so let's sharpen our
  140. focus a bit further and look at what the field editors do:
  141.  
  142.     +----------- 1 -----------+
  143.     |       draw string       | <---+ <--------+
  144.     +-------------------------+     |          |
  145.     +----------- 2 -----------+     |          |
  146.     |    get next command     |     |          |
  147.     +-------------------------+     |          |
  148.     +----------- 3 -----------+     |          |
  149.     |     process command     |     |          |
  150.     +-------------------------+     |          |
  151.           |             |           |          |
  152.         exit         editing        |          |
  153.        command       command        |          |
  154.           |             +-----------+          |
  155.           v                                    |
  156.     +----------- 4 -----------+    +---- 6 ----------------+
  157.     | call validation routine |    | call pre-edit routine |
  158.     +-------------------------+    +-----------------------+
  159.           |             |                      |
  160.         input         input        +---- 5 ----------------+
  161.        accepted      rejected ---> |  call error handler   |
  162.           |                        +-----------------------+
  163.           v
  164.  
  165. Each of the three field editors has a large main loop. At the top of the loop,
  166. the string being edited is drawn and the cursor is moved into position (box
  167. 1). The next step is to get a command from the user. Here again, the
  168. programmer has an opportunity to gain temporary control. By changing the value
  169. of the global variable EntryKeyPtr, you can set up a customized keyboard input
  170. routine (which we'll call "GetKey"). The GetKey routine's main job is to
  171. return the next keystroke entered by the user, but it can do any number of
  172. things while it is waiting for that event if it does them quickly enough. This
  173. facility is known as the "Background Task Hook," and it is discussed at length
  174. in Appendix C (starting on p. 14-10).
  175.  
  176. Once the keyboard handler has returned the next key and it has been translated
  177. into a command (again, see the section on "Installable Keyboard Hooks" in
  178. Appendix C), the field editor must process that command. Here again there are
  179. two broad categories of commands: exit commands and editing commands. Editing
  180. commands are commands that modify the contents of the field and/or move the
  181. cursor from one part of it to another. The category of exit commands comprises
  182. both the "true" exit commands (those that are treated as exit commands by
  183. EditScreen) and those commands that cause the cursor to be moved from one
  184. field to another.
  185.  
  186. When an exit command is given, the field editor will do one of two things. If
  187. <Esc> was pressed (corresponding to the ESquit command), it will restore the
  188. original value of the string being edited and exit immediately. Otherwise, it
  189. will call the "validation routine" associated with the current field. (For
  190. examples of validation routines, see TPENTRY.IN1 and ENTRY.PAS.) The task of
  191. the validation routine varies depending on the data type, but in general it
  192. must do two things: convert the string being edited back to its native type,
  193. then determine whether or not the value entered is acceptable. If the value is
  194. acceptable, it will return True, telling the field editor to return control to
  195. EditScreen. If it is unacceptable, it will return False, passing back an error
  196. code (see p. 3-27) and a pointer to an error message. The field editor will
  197. then either call the user-defined error routine if there is one (see p. 3-20)
  198. or "ring the bell" if there isn't. (In most applications you will probably
  199. want to take advantage of this hook, because the user will rarely understand
  200. what he did wrong if you don't display the error message.) The field editor
  201. will then call the pre-edit routine (if there is one) again, in case the help
  202. message was overwritten by the error message, and go back to the start of the
  203. loop (box 1). This process will be repeated until a valid entry is made by the
  204. user or <Esc> is pressed.
  205.  
  206. That pretty much covers the major components of the data entry system provided
  207. in TPENTRY. It is a complex system, to be sure, but we hope that this overview
  208. will give you the confidence to explore it further. After you've worked with
  209. it a while, we think you'll agree that the system's uncommon flexibility is
  210. responsible for its complexity, and that ultimately the end justifies the
  211. means.
  212.  
  213. Scrollable Entry Screens
  214. ------------------------
  215.  
  216. As of version 5.04, TPENTRY provides improved support for scrollable data
  217. entry screens. Some of these enhancements can be easily quantified:
  218.  
  219.   Limit               5.04 or higher  5.00-5.03
  220.   -----------------   --------------  ---------
  221.   Max fields/screen   2000            255
  222.   Max rows            255             127
  223.   Max columns         255             80
  224.   Scrolling           Horizontal/     Vertical
  225.                       Vertical        only
  226.  
  227. In making these enhancements, we have also managed to increase the performance
  228. of TPENTRY considerably, principally through the use of virtual screens
  229. (allowing for faster, smoother scrolling).
  230.  
  231. The price to be paid for these improvements is, of course, increased memory
  232. usage. With all features of TPENTRY enabled, the same program compiled with
  233. 5.04 will be roughly 3K larger than it would have been if compiled with an
  234. earlier version. In addition, a program will use more heap space than it would
  235. have previously, because the virtual screen that is used to hold the image of
  236. the entry screen is allocated on the heap. (The amount of memory required for
  237. the virtual screen depends on the number of rows and columns in the entry
  238. screen.) Since most applications don't require scrollable entry screens, we
  239. have therefore used conditional compilation directives to prevent the code
  240. that handles scrolling from being pulled in when it isn't needed.
  241.  
  242. So, if you wish to create scrollable entry screens, you must (1) load
  243. TPDEFINE.INC into your editor, (2) delete the '.' in
  244.  
  245.  {.$DEFINE TpEntryScrolls}
  246.  
  247. at the end of the file, (3) save the file, and (4) recompile TPENTRY.PAS. You
  248. can then create scrollable entry screens simply by specifying row and/or
  249. column coordinates that are outside the bounds of the entry screen's window.
  250. (By default, the entire screen is the entry window; to limit it, use
  251. SetEntryWindow.) TPENTRY will handle everything else automatically.
  252.  
  253. When TpEntryScrolls is unDEFINEd, as it is by default, programs that use
  254. TPENTRY will actually be smaller now than they would have been in the past,
  255. even though TPENTRY now pulls in TPWINDOW (whether it uses it or not). You
  256. will not, however, be able to create scrollable entry screens, even of the
  257. older, slower variety. If you try, your program will abort as soon as you try
  258. to add a field that cannot be displayed within the entry window.
  259.  
  260. To give you an idea of what a large scrollable entry screen looks like, we
  261. have included a new demo program, BEST.PAS (Big Entry Screen Test). It doesn't
  262. do anything useful; it just shows you a little bit of what TPENTRY can now do.
  263. The only special features worth noting are
  264.  
  265.   - <AltS> lets you toggle between line- and page-based scrolling (see the
  266.     discussion of ScrollByPage, below).
  267.   - The entry in the first field determines how many rows below it are
  268.     visible. (This is to demonstrate a technique described below in the
  269.     section on Hidden and Semi-Hidden Fields.)
  270.  
  271. Before trying to compile BEST.PAS, be sure to DEFINE TpEntryScrolls.
  272.  
  273. More enhancements:
  274.  
  275.   {$IFDEF TpEntryScrolls}
  276.   const
  277.     ScrollByPage : Boolean = False;
  278.   {$ENDIF}
  279.  
  280.   This typed constant allows you to select an alternate mode of scrolling. If
  281.   ScrollByPage is True and the cursor is moved to a field above or below the
  282.   edges of the entry window, the entry screen is scrolled by one full page (or
  283.   more) rather than by a single line. So, if the window is 20 rows high, the
  284.   cursor is on the 20th row, and you press <Down> to get to a field on row 21,
  285.   rows 21-40 would scroll into view. If ScrollByPage is False, as it is by
  286.   default, rows 2-21 would be scrolled into view instead. Besides limiting the
  287.   amount of scrolling that has to occur, this option also facilitates the
  288.   creation of entry screens in which related fields are grouped together in
  289.   distinct "pages".
  290.  
  291.   const
  292.     ESpageUp     = 30;  {Previous page}
  293.     ESpageDown   = 31;  {Next page}
  294.  
  295.   These two new commands perform one of two functions, depending on whether or
  296.   not an entry screen scrolls vertically. If it doesn't (either because there
  297.   aren't enough rows, or because TpEntryScrolls isn't defined), ESpageUp will
  298.   move the cursor to the first row on the entry screen, and ESpageDown will
  299.   move it the last row. If it does scroll vertically, ESpageUp will move the
  300.   cursor to the top of the previous page, and ESpageDown will move it to the
  301.   top of the next page. Note, however, that by default these commands are not
  302.   assigned to any particular keys. If you want to use them, you must assign
  303.   them to keys--presumably <PgUp> and <PgDn>--using AddEntryCommand (see
  304.   Appendix C), like so:
  305.  
  306.     {assign ESpageUp to <PgUp>, and ESpageDown to <PgDn>}
  307.     if not AddEntryCommand(ESpageUp,   1, $4900, 0) then ;
  308.     if not AddEntryCommand(ESpageDown, 1, $5100, 0) then ;
  309.  
  310.   Keep in mind, however, that doing so will disable the ESnextRec and
  311.   ESprevRec commands--normally assigned <PgDn> and <PgUp>, respectively--so
  312.   you will probably want to reassign these commands to new keys at the same
  313.   time.
  314.  
  315.   Note: In adding these commands, we have had to reassign several existing
  316.   commands to higher numbers. Although these changes could affect existing
  317.   programs, they are not likely to as long as commands are referred to by name
  318.   rather than value (e.g., 'ESquit' rather than '42' or '44'). The only
  319.   obvious exception would be a program modelled on the TPKEYS demo that allows
  320.   the user to reassign commands to new keys. To try to prevent any disasters,
  321.   we have changed the ID string used to mark the start of TPENTRY's key array
  322.   from
  323.  
  324.      EntryKeyID : string[17] = 'tpentry key array';
  325.  
  326.   to
  327.  
  328.      EntryKeyID : string[17] = 'tpentry keys-5.04';
  329.  
  330.   We have also updated TPKEYS to reflect these changes.
  331.  
  332.  
  333.   {$IFDEF TpEntryScrolls}
  334.   procedure CopyVirtualScreenToWindow(var ESR : ESrecord);
  335.     {-Copy the virtual screen to the edit window}
  336.   {$ENDIF}
  337.  
  338.   This procedure, intended primarily for internal use, is interfaced for use
  339.   by programs that need to write to the virtual screen associated with a
  340.   particular ESrecord (entry screen record), then update the physical screen.
  341.   If you really need to do this, you should study the source code in
  342.   TPENTRY.PAS. We will tell you, however, that you need to do something like
  343.   this:
  344.  
  345.       {switch to the virtual screen}
  346.       ActivateVScreen(ESR.VS);
  347.  
  348.       {** write to the virtual screen--see p. 2-68 **}
  349.  
  350.       {switch back to the physical screen}
  351.       DeactivateVScreen;
  352.  
  353.       {update the physical screen}
  354.       CopyVirtualScreenToWindow(ESR);
  355.  
  356.    Please keep in mind that (1) CopyVirtualScreenToWindow does not check to be
  357.    sure that a virtual screen is actually in use and (2) the virtual screen
  358.    will always be completely cleared and redrawn each time a call to either
  359.    DrawEditScreen or EditScreen is made. Incidentally, it is not necessary to
  360.    hide the mouse prior to calling CopyVirtualScreenToWindow or to unhide it
  361.    afterward--that is done automatically if UseMouse is defined.
  362.  
  363.    More importantly, you should also realize that the virtual screen will
  364.    normally not be created until one of those two procedures has been called
  365.    (EditScreen calls DrawEditScreen). It is *not* created by InitESrecord, as
  366.    you might expect, because the bounds of the window are not known until all
  367.    the fields have been added. Instead, it will be created the first time that
  368.    it is needed by DrawEditScreen. If there is not enough memory to allocate
  369.    it, TPENTRY will simply abort the program with an out-of-memory error. If
  370.    this behavior is unacceptable, you should call the following procedure
  371.    *after* adding the last field to your entry screen, *after* any calls to
  372.    SetEntryWindow, and *before* the first call to either DrawEditScreen or
  373.    EditScreen:
  374.  
  375.     {$IFDEF TpEntryScrolls}
  376.     function EntryScrollCheck(var ESR : ESrecord) : Boolean;
  377.       {-Determine if window is scrollable. If so, allocate and initialize
  378.         virtual screen. Returns False only in case of insufficient memory}
  379.     {$ENDIF}
  380.  
  381.    This function determines whether or not an entry screen is scrollable and,
  382.    if it is, allocates a virtual screen for it. It will return False only if
  383.    the entry screen is scrollable *and* there is insufficient memory to
  384.    allocate the virtual screen for it. It returns True if a virtual screen was
  385.    allocated successfully, or if none is needed. In a well-designed program
  386.    that includes scrollable entry screens, you will probably want to call this
  387.    function yourself so that you can handle the error more gracefully,
  388.    especially if your program uses lots of heap space.
  389.  
  390. A few other words of warning:
  391.  
  392. (1) Despite all the improvements, TPENTRY can still get bogged down if the
  393.     entry screen has a large number of fields, partly due to the flexibility
  394.     inherent in TPENTRY's design (the flexibility that allows for
  395.     user-definable data types, validation/conversion routines, picture masks,
  396.     etc.). On a '286 machine, performance is generally bearable with 400
  397.     fields, as BEST.PAS demonstrates, but you'll still see some brief delays
  398.     when the entire entry screen has to be redrawn. On slower machines, the
  399.     threshold of pain will be somewhat lower. We therefore continue to
  400.     recommend that you break large entry screens up into multiple, nested
  401.     entry screens if at all possible.
  402.  
  403. (2) You should also keep in mind that, although TPENTRY now allows up to 255
  404.     rows and up to 255 columns, you cannot create an 255 x 255 entry screen.
  405.     Those limits are really somewhat arbitrary (as is the limit of 2000 fields
  406.     per entry screen). The actual limit is imposed by Turbo Pascal's 64K limit
  407.     on individual data structures. Since the virtual screen now used to store
  408.     an image of the entire entry screen cannot exceed that limit, the product
  409.     of the highest row and column values for the entry screen cannot exceed
  410.     32,760 ($FFF1 div 2). In all likelihood, though, performance will become a
  411.     major issue long before you reach any of these limits.
  412.  
  413. (3) Horizontal scrolling of the entry screen, if it is necessary at all, takes
  414.     place only when you move from one field to another. It will never occur
  415.     while editing a particular field. Although TPENTRY tries to insure that
  416.     the full edit field can be displayed within the edit window, it will not
  417.     generate an error if the edit field is wider than the window. It is up to
  418.     you to insure that this is not the case. (Keep in mind, however, that the
  419.     string editor in TPENTRY can do horizontal scrolling of a different sort
  420.     when you need to edit strings longer than the width of the window. See the
  421.     discussion of the EditLen parameter to AddStringField, on p. 3-43.)
  422.  
  423. (4) In order to implement the enhancements described above, numerous minor
  424.     changes had to be made both to internal data structures and to the
  425.     parameter lists for certain procedures. In general, all variables and
  426.     parameters referring to field ID's (the value assigned to a field in an
  427.     entry screen) or entry screen coordinates (the row and column where a
  428.     field is displayed) were changed from Bytes to Words. For most existing
  429.     applications, these changes should be completely transparent. There is one
  430.     important "gotcha" here, though. If your entry screen contains a multiple
  431.     choice field whose field ID is > 255, you will need to modify your
  432.     "IncValue" routine (see p. 3-17) so that the ID parameter is a Word rather
  433.     than a Byte.
  434.  
  435. Other New Routines
  436. ------------------
  437.  
  438. Declaration
  439.   procedure ChangeFieldAttr(var ESR : ESrecord; FieldID : Word; A : Byte);
  440. Purpose
  441.   Change the field attribute for the specified field.
  442. Comments
  443.   This routine allows you to change the field attribute for a field after it
  444.   has been added to the entry screen, perhaps while within a post-edit
  445.   routine.
  446.  
  447.   ESR is the entry screen, FieldID is the field to change, and A is the new
  448.   attribute. See the section on Hidden and Semi-Hidden Fields, below, for a
  449.   discussion of what this routine can be used for. Note that a call to this
  450.   procedure will change the attribute used to display the field even if it is
  451.   protected.
  452.  
  453.  
  454. Declaration
  455.   procedure ChangePromptAttr(var ESR : ESrecord; FieldID : Word; A : Byte);
  456. Purpose
  457.   Change the prompt attribute for the specified field.
  458. Comments
  459.   This routine allows you to change the prompt attribute for a field after it
  460.   has been added to the entry screen, perhaps while within a post-edit
  461.   routine.
  462.  
  463.   See the section on Hidden and Semi-Hidden Fields, below, for a discussion
  464.   of what this routine can be used for. Note that a call to this procedure
  465.   will change the attribute used to display the prompt even if the field is
  466.   protected.
  467.  
  468. Declaration
  469.   procedure ChangeProtectionFast(var ESR : ESrecord;
  470.                                  FieldID : Word;
  471.                                  OnOff : Boolean);
  472. Purpose
  473.   Modify the protection status of a field.
  474. Comments
  475.   This routine performs the same basic function as ChangeProtection, except
  476.   that it does not call ResetEntryScreenFlags. It is intended to be used in
  477.   place of ChangeProtection in cases where you are changing the protection
  478.   status of multiple fields at a time. Once all the calls to
  479.   ChangeProtectionFast have been made, you *must* call ResetEntryScreenFlags
  480.   *if* this routine is being called from within a post-edit routine.
  481.  
  482.   For further details, see the discussion of ResetEntryScreenFlags. For an
  483.   example of a case where ChangeProtectionFast is needed, see BEST.PAS.
  484.  
  485.  
  486. Declaration
  487.   procedure ChangeRequired(var ESR : ESrecord;
  488.                            FieldID : Word;
  489.                            OnOff : Boolean);
  490. Purpose
  491.   Modify the required status of a field after it has been added.
  492. Comments
  493.   This routine is identical to the ChangeProtection routine described on page
  494.   3-47 of the manual, except that it changes a field's required status rather
  495.   than its protection status.
  496.  
  497.  
  498. Declaration
  499.   procedure ChangeStringAttr(var ESR : ESrecord; FieldID : Word; A : Byte);
  500. Purpose
  501.   Change the string attribute for the specified field.
  502. Comments
  503.   This routine allows you to change the string attribute for a field after it
  504.   has been added to the entry screen, perhaps while within a post-edit
  505.   routine.
  506.  
  507.   See the section on Hidden and Semi-Hidden Fields, below, for a discussion
  508.   of what this routine can be used for.
  509.  
  510.  
  511. Declaration
  512.   procedure ChangeValidation(var ESR  : ESrecord;
  513.                              FieldID  : Word;
  514.                              Validate : Pointer);
  515. Purpose
  516.   Change the validation routine for the specified field.
  517. Comments
  518.   This routine allows you to alter the validation routine for any field in an
  519.   entry screen. ESR is the entry screen. FieldId is the ID number for the
  520.   field. Validate is the address of the validation routine. You might use
  521.   ChangeValidation to specify a special validation routine for a field of type
  522.   Real, for example. Or you might want to use two different validation
  523.   routines for a given field, with the choice of routines depending on whether
  524.   validation needs to be strict or relaxed in a given situation.
  525. Example
  526.   ChangeValidation(ESR, 5, @SpecialValidationRoutine);
  527.  
  528.   Change the validation routine pointer for the sixth field in the designated
  529.   entry screen to point to SpecialValidationRoutine.
  530.  
  531.  
  532. Declaration
  533.   function CurrentFieldModified(var ESR : ESrecord) : Boolean;
  534. Purpose
  535.   Return True if current field was modified.
  536. Comments
  537.   This routine is intended to be used within a post-edit routine (only!) to
  538.   determine whether the current field was actually modified by the user. It is
  539.   especially useful in applications where the post-edit routine must load data
  540.   from disk based on the value of a particular field, and you don't want to
  541.   reread the same data unnecessarily. See also the discussion of
  542.   EvaluateEScommand, below.
  543.  
  544.   Note that, if the current field is a multiple choice field,
  545.   CurrentFieldModified will return True only if the user has actually left the
  546.   field. (The multiple choice editor calls the post-edit routine each time the
  547.   user increments or decrements the value of the field. This allows calculated
  548.   fields that depend on the current field to be updated constantly.)
  549.  
  550.  
  551. Declaration
  552.   function EvaluateEScommand(var ESR : ESrecord; ESC : EStype) : FieldRecPtr;
  553. Purpose
  554.   Given a command, return a pointer to the field the cursor will move to.
  555. Comments
  556.   This routine is intended to be called only from within a post-edit routine.
  557.   Given an entry screen record (ESR) and the code for the last command entered
  558.   by the user (stored in the global variable LastEntryCommand), it calculates
  559.   where EntryScreen will be moving the cursor after the post-edit routine
  560.   returns control. If the cursor is not going to be moved at all, the function
  561.   result will equal ESR.CurrentField. Otherwise, it will point to the field to
  562.   move to.
  563.  
  564.   EvaluateEScommand is especially useful in cases where a post-edit routine
  565.   needs to load additional data from disk after the user has entered a value
  566.   in a particular field. For example, after the user enters a customer ID
  567.   number, the post-edit routine might need to go to disk to load the rest of
  568.   the known data concerning that customer (name, address, etc.).
  569. Example
  570.   {$F+}
  571.   procedure PostEditRoutine(var ESR : ESrecord);
  572.   var
  573.      FRP : FieldRecPtr;
  574.   begin
  575.     if (ESR.CurrentID = FieldOfInterest) then
  576.       if CurrentFieldModified(ESR) then begin
  577.         FRP := EvaluateEScommand(ESR, LastEntryCommand);
  578.         case FRP^.FieldID of
  579.           5..8 :
  580.             begin
  581.               {load some more data}
  582.               ...
  583.               DrawEditScreen(ESR);
  584.             end;
  585.         end;
  586.       end;
  587.   end;
  588.   {$F-}
  589.  
  590.   This sample post-edit routine checks first to see if the cursor is on a
  591.   field of interest, then insures that the user actually modified the field,
  592.   then calls EvaluateEScommand. If the cursor is going to be moved into a
  593.   particular region of the entry screen (fields 5-8), it loads new data from
  594.   disk then redraws the entry screen.
  595.  
  596.  
  597. Declaration
  598.   procedure ResetEntryScreenFlags(var ESR : ESrecord);
  599. Purpose
  600.   Sets/clears a series of flags used internally by TPENTRY.
  601. Comments
  602.   This routine is intended primarily for internal use, but it is interfaced
  603.   for use by the programmer in cases where ChangeProtectionFast is being
  604.   called from within a post-edit routine. This routine does not need to be
  605.   called except in that one very specific case. In all other cases where it
  606.   needs to be called, TPENTRY will call it itself.
  607.  
  608.   What this routine does (in case you are interested) is to follow the linked
  609.   list of fields that make up an entry screen, setting flags to mark those
  610.   fields that are on the first or last row of the entry screen. These flags
  611.   simplify and speed up certain operations.
  612.  
  613.   For an example of a case where both ResetEntryScreenFlags and
  614.   ChangeProtectionFast are needed, see BEST.PAS. There is also a brief
  615.   discussion of such cases in the section on Hidden and Semi-Hidden Fields,
  616.   below.
  617.  
  618.  
  619. Declaration
  620.   procedure SetAllFieldLinks(var ESR : ESrecord; var LinksMap);
  621. Purpose
  622.   Set the forward and backward links for all fields in an entry screen.
  623. Comments
  624.   This routine allows you to modify the order in which fields are "visited" as
  625.   you move through an entry screen by specifying an explicit "forward link"
  626.   for each field. (The forward link is the field to move the cursor to when
  627.   <Enter> is pressed.)
  628.  
  629.   ESR is the entry screen whose forward links are to be modified. The variable
  630.   passed as the LinksMap parameter should be an 'array[0..n] of Word', where n
  631.   is the ID number of the last field in the entry screen. Each element in the
  632.   array specifies the forward link for that field: LinksMap[0] has the forward
  633.   link for field 0, LinksMap[1] has the forward link for field 1, and so on.
  634.   Note that, given this list of forward links, SetAllFieldLinks will
  635.   automatically calculate the appropriate backward links to insure logical
  636.   behavior when the cursor is being moved backward (with <ShiftTab>, for
  637.   example).
  638.  
  639.   If a particular field is protected, you should set its forward link to
  640.   'BadFieldId' (= $FFFF) *unless* the field in question will later be
  641.   unprotected (by a post-edit routine, perhaps). Note that EditScreen will
  642.   ignore any forward or backward link that points to a protected field and
  643.   revert to the default behavior of moving the cursor to the next/previous
  644.   unprotected field added to the list.
  645.  
  646.   Important point: When setting the backward links for all the fields,
  647.   SetAllFieldLinks assumes that each unprotected field in the entry screen is
  648.   named as the forward link for one of the other fields, and that only one
  649.   field names a given field as its forward link. More specifically, when
  650.   setting the backward link for field N, it scans the list of forward links
  651.   looking for another field that points to N. The first one that it finds
  652.   becomes the backward link for field N. If none is found, the backward link
  653.   is set to BadFieldID, indicating that EditScreen should apply its usual set
  654.   of rules for determining where to move the cursor. In general, then, it is
  655.   best to use SetAllFieldLinks to establish a perfect loop linking all
  656.   unprotected fields in the entry screen together (see the example below),
  657.   then use SetFieldLinks to make any minor changes to individual links (to
  658.   prevent wrapping at the edges of the entry screen, for example, which can be
  659.   done by setting a field's forward or backward link to point to itself).
  660.  
  661.   Note that calls to SetAllFieldLinks have no effect on the behavior of the
  662.   <Up> and <Down> commands. Those commands will always move the cursor to the
  663.   field immediately above or below the current field. If you want <Up> and
  664.   <Down> to move the cursor to the next/previous field, you can reassign them
  665.   to the ESprevField and ESnextField commands, respectively, using the
  666.   AddEntryCommand routine described in Appendix C.
  667. Example
  668.                                     {field: 0  1  2  3  4  5}
  669.     const LinksMap : array[0..5] of Word = (1, 3, 0, 4, 2, BadFieldId);
  670.     ...
  671.     SetAllFieldLinks(ESR, LinksMap);
  672.     ...
  673.     ExitCommand := EditScreen(ESR, 2, False);
  674.  
  675.   Here the entry screen is set up such that the cursor starts at field 2, then
  676.   moves to fields 0, 1, 3, 4 (and then back to 2), in that order. Field 5 is a
  677.   protected field, so no forward link is needed for it.
  678.  
  679.  
  680. Declaration
  681.   procedure SetFieldLinks(var ESR : ESrecord; FieldID, Next, Prev : Word);
  682. Purpose
  683.   Specify the fields to jump to when <Enter> or <ShTab> pressed on a given
  684.   field.
  685. Comments
  686.   This routine allows you to modify the order in which fields are "visited" as
  687.   you move through an entry screen by specifying explicit backward and forward
  688.   links. It may be called at any time after the field associated with FieldID
  689.   has been added to the entry screen, even if the fields denoted by Next and
  690.   Prev have not been added yet.
  691.  
  692.   ESR is the entry screen, and FieldID is the field whose forward and/or
  693.   backward links you want to change. Next is the ID for the field to jump to
  694.   when <Enter> is pressed (or any other event that causes the cursor to be
  695.   moved to the next field). Prev is the ID for the field to jump to when
  696.   <ShiftTab> is pressed (or any other event that causes the cursor to be moved
  697.   to the previous field).
  698.  
  699.   If you want to override only the forward link, you can specify 'BadFieldId'
  700.   (= $FFFF) as the Prev parameter. To override only the backward link, specify
  701.   BadFieldId as the Next parameter. (When a forward or backward link is
  702.   BadFieldId, EditScreen will simply apply its usual set of rules for
  703.   determining where to move the cursor. That is, it will move the cursor to
  704.   the next or previous unprotected field, wrapping to the top or bottom of the
  705.   entry screen if necessary and if the current wrap mode allows it.)
  706.  
  707.   Although SetAllFieldLinks is easier to use when numerous changes must be
  708.   made, SetFieldLinks provides greater flexibility. Multiple fields may safely
  709.   be given the same forward link, for example, and individual links may be
  710.   changed easily at any time--even from within a post-edit routine.
  711.  
  712.   Note that calls to SetFieldLinks have no effect on the behavior of the <Up>
  713.   and <Down> commands. Those commands will always move the cursor to the field
  714.   immediately above or below the current field. If you want <Up> and <Down> to
  715.   move the cursor to the next/previous field, you can reassign them to the
  716.   ESprevField and ESnextField commands, respectively, using the
  717.   AddEntryCommand routine described in Appendix C.
  718. Example
  719.     SetFieldLinks(ESR, 0, 1, 2);
  720.     SetFieldLinks(ESR, 1, 3, 0);
  721.     SetFieldLinks(ESR, 2, 0, 4);
  722.     SetFieldLinks(ESR, 3, 4, 1);
  723.     SetFieldLinks(ESR, 4, 2, 3);
  724.     ...
  725.     ExitCommand := EditScreen(ESR, 2, False);
  726.  
  727.   Here the entry screen is set up such that the cursor starts at field 2, then
  728.   moves to fields 0, 1, 3, 4 (and then back to 2) in that order. Contrast this
  729.   example with the one for SetAllFieldLinks, above, which does the same thing.
  730.  
  731.  
  732. Declaration
  733.   procedure SetProtectAttrs(PromptA, FieldA : Byte);
  734. Purpose
  735.   Set prompt and field attributes to be used for protected fields.
  736. Comments
  737.   This routine allows you to specify the attributes to be used when displaying
  738.   protected fields. When using it, you should keep in mind that subsequent
  739.   calls to SetPromptAttr and/or SetFieldAttr will override the settings
  740.   specified here. (In order to avoid breaking existing programs, those two
  741.   routines have to set the default attributes for both protected and
  742.   unprotected fields.)
  743. Examples
  744.     SetProtectAttrs(7, 7);
  745.     SetPromptAttr($B);
  746.     SetFieldAttr($F);
  747.  
  748.   Wrong! The settings specified in the call to SetProtectAttrs will be
  749.   overridden by the subsequent calls to SetPromptAttr and SetFieldAttr.
  750.  
  751.     SetPromptAttr($B);
  752.     SetFieldAttr($F);
  753.     SetProtectAttrs(7, 7);
  754.  
  755.   Correct.
  756.  
  757.  
  758. Declaration
  759.   procedure SetUpdatePtr(var ESR : ESrecord; P : Pointer);
  760. Purpose
  761.   Set pointer to routine to call after entire entry screen is redrawn.
  762. Comments
  763.   This routine, added in version 5.07, allows you to specify the address of
  764.   a procedure that will be called each time that the specified entry screen
  765.   (ESR) is redrawn in its entirety. Your procedure should be of the form:
  766.  
  767.     {$F+}
  768.     procedure MyUpdateProc(var ESR : ESrecord);
  769.     begin
  770.     end;
  771.     {$F-}
  772.  
  773.   The primary purpose for this hook is to allow ordinary text strings to be
  774.   displayed within a scrollable entry screen.
  775.  
  776.   If ESR is indeed scrollable, then its virtual screen will be active at the
  777.   time your update procedure is called. You may write text to the virtual
  778.   screen using FastWrite or any of its kin (see the TPWINDOW chapter of the
  779.   manual for a discussion of how to write to virtual screens). For example,
  780.  
  781.      FastWrite('------ Divider ------', 75, 20, $07);
  782.  
  783.   would write the specified text to row 75 of the virtual screen.
  784.  
  785.   If ESR is not scrollable, your update procedure will be writing to the
  786.   physical screen, rather than a virtual screen. Please note that, although an
  787.   entry screen has window coordinates associated with it, TPENTRY does not
  788.   call the Window() procedure in TPCRT, so you should not use routines that
  789.   take window-relative coordinates (such as FastWriteWindow) to display text
  790.   inside the entry window. Use routines (such as FastWrite) that take absolute
  791.   coordinates instead.
  792.  
  793. Hidden and Semi-Hidden Fields
  794. -----------------------------
  795. Several customers have requested ways to temporarily hide and unhide fields,
  796. and to suppress the display of a field's value in certain situations. In
  797. versions prior to 5.05, such things were difficult to do, but with the
  798. additions of the ChangeFieldAttr, ChangePromptAttr, and ChangeStringAttr
  799. procedures it is now relatively easy to handle these cases. Below are
  800. descriptions of some common situations and brief explanations of how to handle
  801. them. Note that all these descriptions assume that you have read the section
  802. in the manual concerning Calculated Fields (where the concept of the post-edit
  803. routine is introduced).
  804.  
  805.  (1) You have a calculated field of type Real used to display profit/loss.
  806.      If the value is negative, you want the field to be displayed in red; if
  807.      it is positive, in black. The solution here is to have your post-edit
  808.      routine (which is already calculating the value of the field) call
  809.      ChangeFieldAttr to set the field attribute based on the current value. If
  810.      you're initializing the entry screen once and using it to edit multiple
  811.      records (as is done in ENTRY.PAS), don't forget to initialize the field
  812.      attribute each time you go to edit a new record.
  813.  
  814.  (2) Same as (1), but the field is not protected, and you want the field
  815.      displayed in red (if it is negative) even while editing it. In this case
  816.      you would call ChangeStringAttr as well as ChangeFieldAttr.
  817.  
  818.  (3) You have a numeric field whose initial value is 0, and you want the field
  819.      to appear as a complete blank until its value is non-zero. The solution
  820.      to this problem is demonstrated by ENTRY.PAS in its handling of the Wage
  821.      field, and it is essentially identical to that described for (1) above.
  822.      The main difference is in the choice of attributes. When the field's
  823.      value is 0, you simply set the foreground portion of the field attribute
  824.      equal to the background color: if the background color were black, you
  825.      would use $00; if it were blue, you would use $11; if it were light gray,
  826.      you would use $77; and so on. When the field's value is non-zero, you
  827.      change the field attribute to something that will make the contents of
  828.      the field visible. In either case, you would then redraw the field with
  829.      DrawEditField. (See the SetWageFieldAttribute and UpdateHandler
  830.      procedures in ENTRY.PAS.)
  831.  
  832.  (4) You have a calculated field that you want to hide completely when its
  833.      value is 0. The solution is essentially the same as the one for (3),
  834.      except that you need to hide the prompt (using ChangePromptAttr) as well
  835.      as the field itself. ENTRY demonstrates the technique in its handling of
  836.      the Age field. (See the SetAgeFieldAttribute and UpdateHandler procedures
  837.      in ENTRY.PAS.)
  838.  
  839.  (5) You have a field that is needed only if the value in one of the other
  840.      fields is within a certain range. When it is not needed, you don't want
  841.      it displayed at all. The solution here is similar to the one for (4),
  842.      except that you also need to use ChangeProtection to protect the field
  843.      when it is hidden and unprotect it when it is unhidden.
  844.  
  845.  (6) Your entry screen has a variable number of fields, with the actual number
  846.      of fields depending on a value entered by the user in one of the fields
  847.      in the same entry screen. Although this sounds like a complex case, it is
  848.      essentially the same as case (5), except that there are multiple fields
  849.      to be hidden/unhidden at a time rather than one, and consequently you
  850.      need to use ChangeProtectionFast and ResetEntryScreenFlags rather than
  851.      ChangeProtection. For an example of how to handle this situation, see the
  852.      UpdateHandler and HideFields routines in BEST.PAS.
  853.  
  854. Validation Routines Revisited
  855. -----------------------------
  856.  
  857. When writing the manual, we underestimated the amount of information users
  858. would want about how to write custom validation routines. Our basic advice on
  859. this topic remains the same: the best way to learn how to write them is to
  860. study the built-in validation routines. If possible, find one that is close to
  861. what you need, copy it into your program, then modify it to suit your needs.
  862.  
  863. We admit, however, that there is one fairly common case that is not addressed
  864. by any of the standard validation routines, the case where an entry is valid
  865. only if it appears in a list of acceptable answers. We have therefore modified
  866. ENTRY.PAS to include an example of how to write this kind of validation
  867. routine.
  868.  
  869. If you look at the ValidState routine, you'll see that (among other things) it
  870. scans a list of valid state abbreviations checking to see if the user's entry
  871. appears in the list. You may also notice that it uses the new global variable
  872. LastEntryCommand (described below) to determine what the last command was. If
  873. it was ESuser1 (<F2>) or ESclickExit (<ClickLeft>), no validation is performed
  874. because those commands indicate that the pick list of state abbreviations is
  875. about to be displayed. Note too that the call to ValidateNotPartial within the
  876. routine is really unnecessary in this case. It is included primarily to
  877. demonstrate how multiple types of validation may be performed within a single
  878. validation routine. If the user enters a single letter (e.g., 'C'), the error
  879. message will be 'Partial entries not allowed here.'. If the field is full but
  880. the abbreviation is invalid (e.g., 'CS'), the error message will be 'Not a
  881. valid abbreviation for a state.'.
  882.  
  883. When writing your own validation routines, don't forget that (1) they must be
  884. compiled under the FAR model (using {$F+}) and (2) they may not be nested
  885. within another procedure. This same rule applies, of course, to any
  886. user-written procedure that is going to be called from a routine in another
  887. unit.
  888.  
  889. Miscellaneous Changes/Enhancements to TPENTRY
  890. ---------------------------------------------
  891.  
  892. In versions prior to 5.03, AutoAdvance was always forced off for entry screens
  893. containing a single field. This has been changed so that the programmer may
  894. decide whether or not AutoAdvance is desirable in such cases. Note, however,
  895. that single-field entry screens are still treated as special cases: cursor
  896. movement commands such as <Left>, <Right>, <CtrlLeft>, <CtrlRight>, <Tab>, and
  897. <ShiftTab> cannot be used to exit from a field as they can in multi-field
  898. entry screens. Note too that the EditString routine, which creates a
  899. single-field entry screen, does force AutoAdvance off.
  900.  
  901. Several customers have called to ask how they can determine, from within a
  902. validation routine, what command was last issued by the user. Because there
  903. was previously no way to do so, in 5.04 we added the following global variable
  904. for this purpose:
  905.  
  906.   const
  907.     LastEntryCommand : EStype = ESnone;
  908.  
  909. LastEntryCommand can also be used for other, more sinister purposes from
  910. within a post-edit routine. If you need to force the cursor to jump to another
  911. field (something that was previously impossible), you can now do so like this:
  912.  
  913.    {cancel the user's last command}
  914.    LastEntryCommand := ESnone;
  915.  
  916.    {jump to new field}
  917.    ESR.CurrentField := FindFieldID(ESR, TargetFieldID);
  918.  
  919. where ESR is the first parameter passed to your post-edit routine, and
  920. TargetFieldID is the ID for the field to jump to. Similarly, you can force an
  921. exit from the screen editor by setting LastEntryCommand to any exit command
  922. (ESquit or ESdone, for example). Neither of these dirty tricks is encouraged,
  923. but you may use them when no cleaner alternative presents itself.
  924.  
  925. As of version 5.06, LastEntryCommand may also be changed from within a
  926. pre-edit routine in order to force a field editor to execute a particular
  927. command. This would allow you, for example, to display a pick list from within
  928. your pre-edit routine, move the user's choice into the variable associated
  929. with the current field, then use the statement
  930.  
  931.   {force the field editor to exit immediately; move cursor to next field}
  932.   LastEntryCommand := ESnextField;
  933.  
  934. to force the field editor to exit immediately and move the cursor to the next
  935. field. It is also now possible (and safe) to change the CurrentField from
  936. within a pre-edit routine (forcing the cursor to jump to another field):
  937.  
  938.    {jump to new field}
  939.    ESR.CurrentField := FindFieldID(ESR, TargetFieldID);
  940.  
  941. But if you do so any command assigned to LastEntryCommand will be ignored
  942. because the field editor will not be called for the field that was current at
  943. the time your pre-edit routine was called.
  944.  
  945. As of version 5.05, the size of the table used to store key assignments
  946. (EntryKeySet) has been increased from 210 to 240.
  947.  
  948. As of version 5.05, the ChangeProtection procedure can now be used safely from
  949. within a post-edit routine on *any* field. The warnings concerning its use
  950. that appear in the manual on page 3-47 no longer apply.
  951.  
  952. As of version 5.07, you can suppress the display of 0's in empty time fields
  953. by initializing the variable in question to BadTime, and setting both the
  954. minimum and maximum values for the field to MinTime (0). You can do the same
  955. thing with date fields as well, as is demonstrated in ENTRY.PAS: initialize
  956. the date variable to BadDate, and set the minimum and maximum values for the
  957. field to MinDate (0).
  958.  
  959. As of version 5.07, it is now possible to use a picture mask such as 'mm/yy'
  960. for date fields. A day number of 1 will be assumed. As before, you may also
  961. use a picture mask such as 'mm/dd'; the global variable DefaultYear
  962. (initialized to the current year) specifies the year that will be assumed.
  963.