home *** CD-ROM | disk | FTP | other *** search
/ C!T ROM 2 / ctrom_ii_b.zip / ctrom_ii_b / PROGRAM / CLIPPER / PDRD1 / READ.DOC < prev    next >
Text File  |  1992-12-01  |  39KB  |  1,128 lines

  1. The FRANKIE ADread() Function Version 1.1
  2. COPYRIGHT Angelito Dizon, 1992.  All Rights Reserved 
  3.  
  4. To compile and link the examples below, cut and paste them to
  5. appropriate .PRG files then run either of these batch files:
  6.  
  7.     CB <PrgFile> if you use BLINKER
  8.     CR <PrgFile> if you use RTLINK
  9.  
  10.     Make sure that CLIPPER.EXE, BLINKER.EXE and RTLINK.EXE are
  11.     in your DOS path;  READ.LIB and the Clipper libraries
  12.     are in your LIB path; and READ.CH and INKEY.CH are in
  13.     your INCLUDE path.
  14.  
  15. IT IS SUGGESTED THAT YOU RUN THE EXAMPLES _BEFORE_ READING THE
  16. REST OF THE DOCUMENTATION.
  17.  
  18. DESCRIPTION
  19. ===========
  20.  
  21. ADread() is a mouseable and configurable replacement of the
  22. READ command or READMODAL() function.  See READ.KEY for a list
  23. of active keys and mouse spots.
  24.  
  25. Here are some features of ADread():
  26.  
  27. 1.  Click any GET to jump to it.
  28.  
  29. 2.  Expand into memoedit() a memo GET, or any character GET
  30. that is @S-PICTUREd, or that extends beyond the screen
  31. boundaries.
  32.  
  33. 3.  Define additional hot keys and/or additional hot spots.
  34.  
  35.  
  36. SYNTAX
  37. ======
  38.  
  39. ADread( <getlist>, [aConfig] ) --> <aRetval>
  40. --------------------------------------------
  41. <getlist> is an array of GET objects to be activated.
  42.  
  43. [aConfig] is an array of configuration attributes.  If this
  44. array is passed, the behaviour of ADread() is reconfigured
  45. accordingly.  See the CONFIGURING ADread() and THE ADread()
  46. CONFIGURATION ATTRIBUTES sections below for a detailed
  47. discussion of how to use [aConfig].
  48.  
  49. <aRetval> is the return value.  It is an array of two elements:
  50.  
  51.     1 - an array of the update status of the GETs.  The number
  52. of elements in this array is equal to the number of GETs.  The
  53. nth element is set to TRUE if the nth GET was updated,
  54. otherwise it is FALSE.
  55.  
  56.     2 - the ADread() exitcode.  The exitcode is the inkey code
  57. of the exit key, or if ADread() was exited other than via an
  58. exit key, any of the following values which are DEFINEd in
  59. READ.CH:
  60.  
  61.     #define RX_NOCONFIRM       -101 
  62.     #define RX_SAVE            -102 
  63.     #define RX_ABORT           -103 
  64.  
  65.     RX_NOCONFIRM is set if ADread() exited because the last GET
  66.     was completely filled, and SET CONFIRM was OFF.
  67.  
  68.     RX_ABORT is set if the Read was programmatically aborted, or
  69.     when the right mouse button was clicked.  See ADr_abort().
  70.  
  71.     RX_SAVE is set if the Read was programmatically saved.  See
  72.     ADr_save().
  73.  
  74.  
  75. AUXILLIARY FUNCTIONS
  76. ====================
  77. ADr_defaults() --> <aConfig>
  78. ----------------------------
  79. Returns an array of default configuration attributes.  This
  80. function is typically used to reconfigure ADread().  See the
  81. CONFIGURING ADread() and THE ADread() CONFIGURATION ATTRIBUTES
  82. sections below.
  83.  
  84. ADr_varid() --> <cGetVarName>
  85. -----------------------------
  86. Returns the name of the current variable name.  If it is an
  87. array name, it returns it in array notation.
  88.  
  89. ADr_varget( [nthGet] ) --> <xGetValue>
  90. --------------------------------------
  91. Returns the current value of any GET in the currently active
  92. getlist.  If [nthGet] is not passed, it defaults to the current
  93. GET.
  94.  
  95. ADr_varput( <xNewValue>, [nthGet] ) --> nil
  96. -------------------------------------------
  97. Updates the value of any GET in the currently active getlist. 
  98. If [nthGet] is not passed, it defaults to the current GET. 
  99. When the GET is updated this way, the display is
  100. correspondingly updated also.  Note that the type of
  101. <xNewValue> must be in agreement with the type of the Get
  102. variable being updated, otherwise a RUN error occurs.
  103.  
  104. ADr_mousify( <getlist> ) --> nil
  105. --------------------------------
  106. This is the function that mousifies the GETS.  It must be
  107. called after every @ .. GET statement.  Alternately, you may
  108. use @ ..ADGET, instead.  This command is #COMMANDed in READ.CH. 
  109. If you use it, you must not call ADr_mousify() anymore. 
  110.  
  111.  
  112. ADr_expget( <getlist>, [aCoords], [aColor], [lLimitLength] )
  113.     --> nil
  114. ------------------------------------------------------------
  115. This function reconfigures the memoedit() attributes of an
  116. expanded GET.  It is called after the corresponding @..ADGET of
  117. the expandable GET.  See also the EXPANDABLE GET NOTES section
  118. below.
  119.  
  120. <getlist> is the array of GET objects
  121.  
  122. [aCoords] is an optional array of memoedit() box coordinates. 
  123. If it is not passed, it defaults to {10,10,20,69}.
  124.  
  125. [aColor] is an optional color specification string for use by
  126. memoedit().  If it is not passed, it defaults to "GR+/W" and
  127. "I", respectively for color and BW monitors.
  128.  
  129. [lLimitLength] is a logical value.  If it is TRUE, ADread()
  130. will limit the length of the return value of memoedit() to the
  131. original length of the GET variable.  Defaults to TRUE. 
  132. Typically, you will want to set this to FALSE for memo fields.
  133.  
  134. NOTE that ADr_expget() is called only if there is a desire to
  135. change the default values.  ADread() always expands expandable
  136. GETS, whether or not ADr_expget() is used or not.
  137.  
  138.  
  139. ADr_save() --> <lExitRead>
  140. --------------------------
  141. Attempts to save and exit ADread().  If there is no VALID
  142. clause, <lExitRead> is TRUE.  If there is a VALID clause,
  143. <lExitRead> is TRUE if the entered value is valid.  Otherwise,
  144. it is FALSE.  ADread() exits if <lExitRead> is TRUE.
  145.  
  146.  
  147. ADr_abort() --> <TRUE>
  148. ----------------------
  149. This function aborts ADread() and always returns a TRUE.
  150.  
  151.  
  152. EXPANDABLE GET NOTES
  153. ====================
  154. 1.  Not all GETs are memoedit expandable.  Only those that are
  155. @S-PICTUREd, or that extend beyond the screen may be expanded.
  156.  
  157. 2.  The default configuration of ADread() calls for the
  158. pressing of [TAB] to expand the current GET.  If a mouse is
  159. active, clicking the focused GET also causes it to expand.  This default
  160. setting may be reconfigured such that the the GET automatically
  161. expands when it is focused.
  162.  
  163. 3.  There is also a default location, size and color of the
  164. memoedit box.  This may also be reconfigured, either globally
  165. for the whole ADread() by passing [aConfig], or locally for
  166. each GET by using ADr_expget().
  167.  
  168. 4.  When in expanded mode, ADread() does not monitor the length
  169. of the  text being MEMOEDITed.  If it exceeds the original
  170. length of the GET, it will be truncated when it is saved,
  171. unless you specify not to do so in ADr_expget().
  172.  
  173. 5.  Note that when a .DBT is APPENDed BLANK, the memo field is
  174. initially "".  Thus, if you GET it right after APPENDing BLANK,
  175. ADread() will not treat it as an expandable GET since it is an
  176. empty string.  Update the value of the memo field first.
  177.  
  178.  
  179.  
  180. CONFIGURING ADread()
  181. ====================
  182. The default behaviour of ADread() may be reconfigured via
  183. the optional [aConfig] parameter.  [aConfig] is an array of a
  184. number of attributes that you can redefine in three easy steps:
  185.  
  186.     1.  Get the default configuration using ADr_defaults(). 
  187.     This auxilliary function returns an array whose structure
  188.     is #DEFINEd in READ.CH.  Such #DEFINition is discussed
  189.     in greater detail in THE ADread() ATTRIBUTES CONFIGURATION
  190.     section below.
  191.  
  192.     2.  Assign new values to the elements of the default array
  193.     that correspond to the attributes you want to reconfigure.
  194.  
  195.     3.  Pass the array to ADread().
  196.  
  197. See also ADr_expget() for locally reconfiguring the memoedit
  198. box location and color of a specific expandable GET.
  199.  
  200. THE ADread() CONFIGURABLE ATTRIBUTES
  201. ====================================
  202. #define R_CARGO       1
  203. -----------------------
  204. This is a user-defined attribute variable that can be used for
  205. any purpose.  It can be assigned a value of any type.
  206.  
  207. #define R_INITBLOCK   2
  208. -----------------------
  209. This is a code block that ADread() EVALuates just before
  210. setting the focus on the first GET.  It is automatically
  211. passed 3 parameters:
  212.  
  213.     e - a numeric that identifies the ADread engine.  (See THE
  214.         ADread() ENGINE section)
  215.     c - a reference to the array of configuration attributes
  216.     p - a reference to the array of peekable attributes
  217.         (See THE ADread() PEEKABLE ATTRIBUTES section)
  218.  
  219. The default code block is a null code block.
  220.  
  221. #define R_MOVEBLOCK   3
  222. -----------------------
  223. This is a code block that ADread() EVALuates when it changes
  224. focus to another GET.  It is automatically passed 3 parameters:
  225.  
  226.     e - a numeric that identifies the ADread engine.  (See THE
  227.         ADread() ENGINE section)
  228.     c - a reference to the array of configuration attributes
  229.     p - a reference to the array of peekable attributes
  230.         (See THE ADread() PEEKABLE ATTRIBUTES section)
  231.  
  232. The default code block is a null code block.
  233.  
  234. #define R_EXITBLOCK   4
  235. -----------------------
  236. This is a code block that ADread() EVALuates when it receives a
  237. request to exit (either to save or abort). It is automatically
  238. passed 3 parameters:
  239.  
  240.     e - a numeric that identifies the ADread engine.  (See THE
  241.         ADread() ENGINE section)
  242.     c - a reference to the array of configuration attributes
  243.     p - a reference to the array of peekable attributes
  244.         (See THE ADread() PEEKABLE ATTRIBUTES section)
  245.  
  246. It EVALuates to a logical value.  If it returns a TRUE,
  247. ADread() proceeds to exit.  The default code block is a TRUE
  248. code block.
  249.  
  250. #define R_KEYS        5
  251. -----------------------
  252. This is an array of the inkey codes of user-defined hot keys. 
  253. Defaults to an empty array.
  254.  
  255. #define R_KHANDLER    6
  256. -----------------------
  257. This is a code block that ADread() EVALuates when one of the
  258. user-defined keys is pressed.  it is automatically passed 5
  259. parameters:
  260.  
  261.     e - a numeric that identifies the ADread engine.  (See THE
  262.         ADread() ENGINE section)
  263.     c - a reference to the array of configuration attributes
  264.     p - a reference to the array of peekable attributes
  265.         (See THE ADread() PEEKABLE ATTRIBUTES section)
  266.     nth - the index position of the hot key withing the array
  267.         of hot keys
  268.     nkey - the inkey code of the hot key
  269.  
  270. The default code block must return a logical value.  If it
  271. returns a TRUE, ADread() exits.
  272.  
  273. #define R_LBUTTONS    7
  274. -----------------------
  275. This is an array of user-defined mouse left button hot spots. 
  276. A hot spot is an array of 4 numeric values in the format {nTop,
  277. nLeft, nBottom, nRight}.  Defaults to an empty array.
  278.  
  279. #define R_LBHANDLER   8
  280. -----------------------
  281. This is a code block that ADread() EVALuates when one of the
  282. user-defined left button hot spots is clicked.  it is automatically
  283. passed 6 parameters:
  284.  
  285.     e - a numeric that identifies the ADread engine.  (See THE
  286.         ADread() ENGINE section)
  287.     c - a reference to the array of configuration attributes
  288.     p - a reference to the array of peekable attributes
  289.         (See THE ADread() PEEKABLE ATTRIBUTES section)
  290.     nth - the index position of the hot key withing the array
  291.         of hot keys
  292.     mrow - the row position of the mouse cursor
  293.     mcol - the column position of the mouse cursor
  294.  
  295. The default code block must return a logical value.  If it
  296. returns a TRUE, ADread() exits.
  297.  
  298. #define R_RBUTTONS    9
  299. -----------------------
  300. This is an array of user-defined mouse right button hot spots. 
  301. A hot spot is an array of 4 numeric values in the format {nTop,
  302. nLeft, nBottom, nRight}.  Defaults to an empty array.
  303.  
  304. #define R_RBHANDLER   10
  305. -----------------------
  306. This is a code block that ADread() EVALuates when one of the
  307. user-defined right button hot spots is clicked.  it is automatically
  308. passed 6 parameters:
  309.  
  310.     e - a numeric that identifies the ADread engine.  (See THE
  311.         ADread() ENGINE section)
  312.     c - a reference to the array of configuration attributes
  313.     p - a reference to the array of peekable attributes
  314.         (See THE ADread() PEEKABLE ATTRIBUTES section)
  315.     nth - the index position of the hot key withing the array
  316.         of hot keys
  317.     mrow - the row position of the mouse cursor
  318.     mcol - the column position of the mouse cursor
  319.  
  320. The default code block must return a logical value.  If it
  321. returns a TRUE, ADread() exits.
  322.  
  323. #define R_EXPCOORDS   11
  324. -----------------------
  325. This is an array of box coordinates that define the memoedit
  326. box.  Defaults to {10,10,20,69}.  You can also use ADr_expget() to
  327. specify the box coordinates and color of the memoedit box.
  328.  
  329. #define R_EXPCOLOR    12
  330. -----------------------
  331. This is a color specifier for use by memoedit.  It defaults to
  332. "GR+/W" and "I", respectively for color and BW monitors.
  333.  
  334. #define R_AUTOEXPAND  13
  335. -----------------------
  336. This is a logical value.  If it is TRUE, an expandable GET
  337. automatically expands to a memoedit when focus is set to it. 
  338. If it is FALSE, the [TAB] key has to be pressed, or the left
  339. button clicked on it when it is in focus.  Defaults to FALSE.
  340.  
  341. #define R_KEYVALIDBLOCK 14
  342. --------------------------
  343. This points to a code block that ADread() EVALuates before
  344. inserting every key stroke to the Get edit buffer.  The code
  345. block must return a logical value.  If it EVALuates to TRUE,
  346. then the key is inserted, otherwise it is disregarded.  It
  347. is functionally similar to the PICTURE clause, but in a more
  348. refined manner.  It is automatically passed 4 parameters:
  349.  
  350.     cKey - the character that was last pressed
  351.     nPos - the current position of the editing cursor, relative
  352.            to the starting position of the Get screen
  353.     nthGet - the index position, within GetList, of the current
  354.            Get
  355.     cEditBuffer - the current value of the Get edit buffer.
  356.  
  357.  
  358. THE ADread() PEEKABLE ATTRIBUTES
  359. ================================
  360.  
  361. Aside from the configurable attributes, ADread() maintains
  362. another group of attributes, the so-called peekable (I cannot
  363. think of a better term).  These are attributes that ADread()
  364. constantly updates.  They are made available to you via the
  365. configurable code blocks: R_INITBLOCK, R_MOVEBLOCK,
  366. R_EXITBLOCK, R_KHANDLER, R_LBHNADLER, and R_RBHANDLER, in case
  367. you need them.  If you are one who always look for ways of 
  368. extending the power of Clipper, I am sure you will find a use
  369. for these values.  This ADread() feature is in line with
  370. Clipper's open-architecture design.  CAUTION: These values are
  371. intended to be "peeked" only, and not "poked".  If you change
  372. their values, the result is unpredictable.
  373.  
  374. The peekable attributes are #DEFINEd in READ.CH. as follows:
  375.  
  376. #define PKREAD_NTHGET      1
  377. ----------------------------
  378. The index position of the current GET.  (numeric)
  379.  
  380. #define PKREAD_EXITCODE    2
  381. ----------------------------
  382. The exit code of the last attempt to exit ADread().  (numeric)
  383.  
  384. #define PKREAD_NGETS       3
  385. ----------------------------
  386. The number of GETS in getlist.  (numeric)
  387.  
  388. #define PKREAD_INSERT      4
  389. ----------------------------
  390. The current _SET_INSERT_ setting within the context of
  391. ADread().  This may not coincide with the actual Clipper
  392. setting.  (logical)
  393.  
  394. #define PKREAD_UPDATED     5
  395. ----------------------------
  396. An array of logical values.  The size of the array is the
  397. number of GETS.  If a GET has been updated, the corresponding
  398. element in this array is set to FALSE.
  399.  
  400.  
  401. THE ADread() ENGINE
  402. ===================
  403. ADread() is built around an engine.  You may think of an engine
  404. as a pseudo-object, or as a handle.  Every ADread() call
  405. creates an ADread() engine which is used internally.  You may
  406. however gain access to it via the configurable code blocks:
  407. R_INITBLOCK, R_MOVEBLOCK, R_EXITBLOCK, R_KHANDLER, R_LBHNADLER,
  408. and R_RBHANDLER.  In this version of ADread(), however, there
  409. is nothing that you can do with it.  In future versions, there
  410. will be plenty that you can do with it.  You will be able to
  411. "send messages" to it and cause it to do something.  The "open
  412. architecture" of Clipper will be extensively exploited!
  413.  
  414.  
  415. Example 1
  416. ---------
  417. #include "read.ch"
  418. #include "inkey.ch"
  419.  
  420. //---------
  421. func main()
  422. field dbfname, shared, rlocked, date, comments
  423. local xdbfname, xshared, xrlocked, xdate, xcomments
  424. local aScn, nT := 17, nL := 10, nB := 23, nR := 69, getlist[0], aReadRetval
  425. local cColor := setcolor( if( iscolor(), "W+/BG, GR+/R,,, B/BG", ) )
  426.  
  427. use demo
  428. cls
  429. Xinstructions()
  430.  
  431. xdbfname = dbfname
  432. xshared = shared
  433. xrlocked = rlocked
  434. xdate = date
  435. xcomments = comments
  436.  
  437. aScn = ADbox( nT, nL, nB, nR )
  438. // Note in this Get series, 'adget' is used instead of simply 'get'
  439. @nT+1, nL+2 say "Database name         " adget xdbfname picture "@!"
  440. @nT+2, nL+2 say "Opened in shared mode?" adget xshared
  441. @nT+3, nL+2 say "Locked record number  " adget xrlocked 
  442. @nT+4, nL+2 say "Date locked           " adget xdate
  443. @nT+5, nL+2 say "Comments              " adget xcomments picture "@S33"
  444. aReadRetval = ADread( getlist )
  445. // aReadRetval is a two-element array.  Element #2 is the exit
  446. // code of ADread().  Element #1 is an array of the update
  447. // status of each Get
  448.  
  449. if aReadRetval[2] != K_ESC .and. aReadRetval[2] != RX_ABORT;  // IF ADread() was not aborted
  450. .and. ascan( aReadRetval[1], .T. ) > 0                        // .AND. at least one of the Gets was updated   
  451.     dbfname = xdbfname                                        // THEN update the database
  452.     shared = xshared
  453.     rlocked = xrlocked
  454.     date = xdate
  455.     comments = xcomments
  456. endif
  457.  
  458. ? ""
  459. ? "Exit Code: ", aReadRetval[2]
  460. ? " ┌─────── Updated?"
  461. ? " │     ┌─ Updated Values"
  462. ? "      "
  463. ? aReadRetval[1][1], dbfname
  464. ? aReadRetval[1][2], shared
  465. ? aReadRetval[1][3], rlocked
  466. ? aReadRetval[1][4], date
  467. ? aReadRetval[1][5], comments
  468. setcolor( cColor )
  469. return nil
  470.  
  471.  
  472. //------------------
  473. func Xinstructions()
  474. ADbox( 0,0,16,79,,,.F. )
  475. @0,28 say " ADread() Demonstration #1 "
  476. @1,1 say  "Check this out:"
  477. @2,1 say  "  √ Left-click at any Get.  That Get will immediately get focused."
  478. @3,1 say  "  √ Set the focus on the 'Comments' Get, then left-click it.  It will expand"
  479. @4,1 say  "    to a mouseable memoedit."
  480. @5,1 say  "  √ While you are in expanded mode, enter any thing.  Then left-click [-]."
  481. @6,1 say  "    This will save the memoedit and return to the Get.  If you click the"
  482. @7,1 say  "    right button, instead, the memoedit will abort.  The [?] spot is reserved"
  483. @8,1 say  "    for 'Help'.  Note that there's cursor synchronization, as well as insert/"
  484. @9,1 say  "    overwrite modes, between the Get and and the memoedit. The [TAB] key saves"
  485. @10,1 say "    the memoedit, while the [ESC] aborts it."
  486. @11,1 say "  √ You can also expand 'Comments' by pressing the [TAB] key."
  487. @12,1 say "  √ Clicking the right button will abort ADread().  It is the same as pressing"
  488. @13,1 say "    the [ESC] key."
  489. @14,1 say "  √ Move the cursor to the last position of the first Get. Press [RIGHT]; the"
  490. @15,1 say "    focus moves to the next Get.  Press [LEFT]; the focus moves back."
  491. return nil
  492.  
  493.  
  494. Example 2
  495. ---------
  496. #include "read.ch"
  497. #include "inkey.ch"
  498.  
  499. //---------
  500. func main()
  501. field dbfname, shared, rlocked, date, comments
  502. local xdbfname, xshared, xrlocked, xdate, xcomments
  503. local aScn, nT := 17, nL := 10, nB := 23, nR := 69, getlist[0], aReadRetval
  504.  
  505. // Get a reference to the array of default configurable
  506. // attributes, so they can be changed.
  507. local aReadConfig := ADr_defaults()
  508.  
  509. local cColor := setcolor( if( iscolor(), "W+/BG, GR+/R,,, B/BG", ) )
  510.  
  511. use demo
  512. cls
  513. Xinstructions()
  514.  
  515. xdbfname = dbfname
  516. xshared = shared
  517. xrlocked = rlocked
  518. xdate = date
  519. xcomments = comments
  520.  
  521. aScn = ADbox( nT, nL, nB, nR )
  522. @nT+1, nL+2 say "Database name         " adget xdbfname picture "@!"
  523. @nT+2, nL+2 say "Opened in shared mode?" adget xshared
  524. @nT+3, nL+2 say "Locked record number  " adget xrlocked 
  525. @nT+4, nL+2 say "Date locked           " adget xdate
  526. @nT+5, nL+2 say "Comments              " adget xcomments picture "@S33"
  527. ADr_expget( getlist, {0,0,4,50 }, "W+/B", .T. )
  528.  
  529. // causes a memoeditable Get to automatically expand to a memoedit as soon
  530. // as it is focused.
  531. aReadConfig[R_AUTOEXPAND] = .t.
  532.  
  533. // causes ADread() to validate every key press before inserting it to the
  534. // Get buffer.  The user-defined function, Xkeyvalid() controls what type
  535. // of validation to do.
  536. aReadConfig[R_KEYVALIDBLOCK] = {|cKey,nPos,nthGet,cBuffer| Xkeyvalid(cKey,nPos,nthGet,cBuffer)}
  537.  
  538. aReadRetval = ADread( getlist, aReadConfig )
  539.  
  540. if aReadRetval[2] != K_ESC .and. aReadRetval[2] != RX_ABORT;  // IF ADread() was not aborted
  541. .and. ascan( aReadRetval[1], .T. ) > 0                        // .AND. at least one of the Gets was updated   
  542.     dbfname = xdbfname                                        // THEN update the database
  543.     shared = xshared
  544.     rlocked = xrlocked
  545.     date = xdate
  546.     comments = xcomments
  547. endif
  548.  
  549. ? ""
  550. ? "Exit Code: ", aReadRetval[2]
  551. ? " ┌─────── Updated?"
  552. ? " │     ┌─ Updated Values"
  553. ? "      "
  554. ? aReadRetval[1][1], dbfname
  555. ? aReadRetval[1][2], shared
  556. ? aReadRetval[1][3], rlocked
  557. ? aReadRetval[1][4], date
  558. ? aReadRetval[1][5], comments
  559. setcolor( cColor )
  560. return nil
  561.  
  562.  
  563. //----------------------------------------------
  564. func Xkeyvalid( cKey, nPos, nthGet, cGetBuffer )
  565. // This function will accept only October, November and December in Get #4.
  566. // cKey is the key stroke to be validated
  567. // nPos is the current position of the editing cursor
  568. // nthGet is the index position of the current Get object
  569. // cGetBuffer is the current value of the Get editing buffer
  570. local lValid := .t.
  571.  
  572. if nthGet = 4   // this function is applicable only to Get #4
  573.     if nPos = 1
  574.         if cKey != "1"      // accespts only "1" in position #1
  575.             lValid = .f.
  576.         endif
  577.     elseif nPos = 2
  578.         if !cKey $ "012"    // accepts only "0", "1" or "2" in position #2
  579.             lValid = .f.
  580.         endif
  581.     endif
  582. endif
  583.  
  584. return lValid
  585.  
  586.  
  587. //------------------
  588. func Xinstructions()
  589. ADbox( 0,0,10,79,,,.F. )
  590. @0,28 say " ADread() Demonstration #2 "
  591. @1,2 say "This is the same as Demo #1, except that the location, size and color"
  592. @2,2 say "of the expanded memoedit box is changed.  Also, the expansion is automatic."
  593. @3,2 say "You do not have to press the [TAB] key.  Check these out:"
  594. @4,2 say "  √ Move the focus to 'Comments'.  It will automatically expand."
  595. @5,2 say "  √ When you exit the expanded mode, you can re-expand the Get by pressing"
  596. @6,2 say "    the [TAB] key or by moving the focus away from 'Comments' and back."
  597. @7,2 say "  √ Go to 'Date locked'.  You can only enter a '1' in position #1, and only"
  598. @8,2 say "    '0', '1' or '2' in position #2.  This is an enhnacement of the PICTURE"
  599. @9,2 say "    extension of Get."
  600. return nil
  601.  
  602.  
  603. Example 3
  604. ---------
  605. This example demonstrates to define hot keys and hot spots to
  606. the ADread().
  607.  
  608. #include "read.ch"
  609. #include "inkey.ch"
  610.  
  611. //---------
  612. func main()
  613. field dbfname, shared, rlocked, date, comments
  614. local xdbfname, xshared, xrlocked, xdate, xcomments
  615. local aScn, nT := 17, nL := 10, nB := 23, nR := 69, getlist[0], aReadRetval
  616. local aReadConfig := ADr_defaults()
  617. local cColor := setcolor( if( iscolor(), "W+/BG, GR+/R,,, B/BG", ) )
  618.  
  619. use demo
  620. cls
  621. Xinstructions()
  622.  
  623. xdbfname = dbfname
  624. xshared = shared
  625. xrlocked = rlocked
  626. xdate = date
  627. xcomments = comments
  628.  
  629. aScn = ADbox( nT, nL, nB, nR )
  630. @nT+0, nL+2 say "[-][?]";
  631.             color if( iscolor(), "R/BG", "W+/N" )
  632. @nT+1, nL+2 say "Database name         " adget xdbfname picture "@!"
  633. @nT+2, nL+2 say "Opened in shared mode?" adget xshared
  634. @nT+3, nL+2 say "Locked record number  " adget xrlocked 
  635. @nT+4, nL+2 say "Date locked           " adget xdate
  636. @nT+5, nL+2 say "Comments              " adget xcomments picture "@S33"
  637.  
  638. // defines K_F2 as a hot key
  639. aReadConfig[R_KEYS] = { K_F2 }
  640.  
  641. // defines a hot key handler routine
  642. aReadConfig[R_KHANDLER] = {|e,c,p,nth,nk| Xmessage(), .F.}
  643.  
  644. // defines two left button hot spots
  645. aReadConfig[R_LBUTTONS] = {;
  646.                                 {nT, nL+2, nT, nL+4 },;
  647.                                 {nT, nL+5, nT, nL+7 };
  648.                           }
  649.  
  650. // defines a hot spot handler routine
  651. aReadConfig[R_LBHANDLER] = {|e,c,p,nth,mr,mc| Xlbhandler(e,c,p,nth,mr,mc)}
  652.  
  653. aReadRetval = ADread( getlist, aReadConfig )
  654.  
  655. if aReadRetval[2] != K_ESC .and. aReadRetval[2] != RX_ABORT;  // IF ADread() was not aborted
  656. .and. ascan( aReadRetval[1], .T. ) > 0                        // .AND. at least one of the Gets was updated   
  657.     dbfname = xdbfname                                        // THEN update the database
  658.     shared = xshared
  659.     rlocked = xrlocked
  660.     date = xdate
  661.     comments = xcomments
  662. endif
  663.  
  664. ? ""
  665. ? "Exit Code: ", aReadRetval[2]
  666. ? " ┌─────── Updated?"
  667. ? " │     ┌─ Updated Values"
  668. ? "      "
  669. ? aReadRetval[1][1], dbfname
  670. ? aReadRetval[1][2], shared
  671. ? aReadRetval[1][3], rlocked
  672. ? aReadRetval[1][4], date
  673. ? aReadRetval[1][5], comments
  674. setcolor( cColor )
  675. return nil
  676.  
  677.  
  678. //------------------------------
  679. func Xlbhandler(e,c,p,nth,mr,mc)
  680. local lRetval
  681.  
  682. ADm_rwait()
  683.  
  684. if nth = 1      // clicked [-]
  685.     lRetval = ADr_save()
  686. elseif nth = 2  // clicked [?]
  687.     Xmessage()
  688.     lRetval = .F.
  689. endif
  690.  
  691. return lRetval
  692.  
  693.  
  694. //-------------
  695. func Xmessage()
  696. local cColor := if( iscolor(), "B/W", "W+/N" )
  697. local nCursor := setcursor(0), nRow := row(),  nCol := col()
  698. local aScn := ADbox( 10, 9, 12, 70, cColor )
  699.  
  700. if ADr_varid() == "XDBFNAME"
  701.     @11,11 say "Enter the name of a database, without the .DBF extension" color cColor
  702. elseif ADr_varid() == "XSHARED"
  703.     @11,11 say "Enter a TRUE if it is opened in shared mode, else a FALSE" color cColor
  704. elseif ADr_varid() == "XRLOCKED"
  705.     @11,11 say "Enter the record number of the record to lock or check" color cColor
  706. elseif ADr_varid() == "XDATE"
  707.     @11,11 say "Enter the date you think the record may have been locked" color cColor
  708. elseif ADr_varid() == "XCOMMENTS"
  709.     @11,11 say "Enter anything you want. Press [TAB] to expand to memoedit" color cColor
  710. endif
  711.  
  712. ADwait()
  713. ADm_rwait()
  714. ADrestscn( aScn )
  715. setpos( nRow, nCol )
  716. setcursor( nCursor )
  717. return nil
  718.  
  719. //------------------
  720. func Xinstructions()
  721. ADbox( 0,0,5,79,,,.F. )
  722. @0,28 say " ADread() Demonstration #3 "
  723. @1,2 say "This is the same as Demo #1, except that two hot spots and one hot key are"
  724. @2,2 say "defined here.  Check these out:"
  725. @3,2 say "    √ Click [-];  ADread() will save and exit."
  726. @4,2 say "    √ Click [?] or press [F2];  a Get-sensitive message is displayed."
  727. return nil
  728.  
  729.  
  730. Example 4
  731. ---------
  732. This example illustrates how to display GET-sensitive messages.
  733.  
  734. #include "read.ch"
  735. #include "inkey.ch"
  736.  
  737. //---------
  738. func main()
  739. field dbfname, shared, rlocked, date, comments
  740. local xdbfname, xshared, xrlocked, xdate, xcomments
  741. local aScn, aScn2, nT := 14, nL := 10, nB := 20, nR := 69, getlist[0], aReadRetval
  742. local aReadConfig := ADr_defaults()
  743. local cColor := setcolor( if( iscolor(), "W+/BG, GR+/R,,, B/BG", ) )
  744.  
  745. use demo
  746. cls
  747. Xinstructions()
  748.  
  749. xdbfname = dbfname
  750. xshared = shared
  751. xrlocked = rlocked
  752. xdate = date
  753. xcomments = comments
  754.  
  755. aScn = ADbox( nT, nL, nB, nR )
  756. @nT+1, nL+2 say "Database name         " adget xdbfname picture "@!"
  757. @nT+2, nL+2 say "Opened in shared mode?" adget xshared
  758. @nT+3, nL+2 say "Locked record number  " adget xrlocked 
  759. @nT+4, nL+2 say "Date locked           " adget xdate
  760. @nT+5, nL+2 say "Comments              " adget xcomments picture "@S33"
  761.  
  762. // Instruct ADread() to draw a line at he bottom of the screen, and display
  763. // an initial message, before setting focus on the first Get
  764. aReadConfig[R_INITBLOCK] = {|e,c,p| aScn2 := ADsavescn( 23,0,24,79),;   // saves the bottom part of the screen 
  765.                                     scroll( 23,0,24,79,0 ),;            // clears the bottom part of the screen
  766.                                     setpos( 23,0 ),;
  767.                                     dispout( repl( "─", 80 ) ),;        // draws a horizontal line
  768.                                     Xmessage();                         // displays a message
  769.                            }
  770. // Instruct ADread() to display a Get-sensitive message whenever the focus
  771. // changes.
  772. aReadConfig[R_MOVEBLOCK] = {|e,c,p| Xmessage()}
  773.  
  774. // Instruct ADread() to restore the bottom part of the screen at exit.
  775. aReadConfig[R_EXITBLOCK] = {|e,c,p| ADrestscn( aScn2 ), .T.}
  776.  
  777. aReadRetval = ADread( getlist, aReadConfig )
  778.  
  779. if aReadRetval[2] != K_ESC .and. aReadRetval[2] != RX_ABORT;  // IF ADread() was not aborted
  780. .and. ascan( aReadRetval[1], .T. ) > 0                        // .AND. at least one of the Gets was updated   
  781.     dbfname = xdbfname                                        // THEN update the database
  782.     shared = xshared
  783.     rlocked = xrlocked
  784.     date = xdate
  785.     comments = xcomments
  786. endif
  787.  
  788. ? ""
  789. ? "Exit Code: ", aReadRetval[2]
  790. ? " ┌─────── Updated?"
  791. ? " │     ┌─ Updated Values"
  792. ? "      "
  793. ? aReadRetval[1][1], dbfname
  794. ? aReadRetval[1][2], shared
  795. ? aReadRetval[1][3], rlocked
  796. ? aReadRetval[1][4], date
  797. ? aReadRetval[1][5], comments
  798. setcolor( cColor )
  799. return nil
  800.  
  801.  
  802. //-------------
  803. func Xmessage()
  804. local nRow := row(),  nCol := col(), cMsg
  805.  
  806. if ADr_varid() == "XDBFNAME"
  807.     cMsg = "Enter the name of a database, without the .DBF extension"
  808. elseif ADr_varid() == "XSHARED"
  809.     cMsg = "Enter a TRUE if it is opened in shared mode, else a FALSE"
  810. elseif ADr_varid() == "XRLOCKED"
  811.     cMsg = "Enter the record number of the record to lock or check"
  812. elseif ADr_varid() == "XDATE"
  813.     cMsg = "Enter the date you think the record may have been locked"
  814. elseif ADr_varid() == "XCOMMENTS"
  815.     cMsg = "Enter anything you want. Press [TAB] to expand to memoedit"
  816. endif
  817.  
  818. @24,1 say padr( cMsg, 78 )
  819. setpos( nRow, nCol )
  820. return nil
  821.  
  822. //------------------
  823. func Xinstructions()
  824. ADbox( 0,0,5,79,,,.F. )
  825. @0,28 say " ADread() Demonstration #4 "
  826. @1,2 say "This is the same as Demo #1, except ADread() is configured to display GET-"
  827. @2,2 say "sensitive messages at the bottom of the screen.  Move the cursor from GET"
  828. @3,2 say "to GET and see the message change.  The location as well as the 'looks' of"
  829. @4,2 say "the messages are fully configurable."
  830. return nil
  831.  
  832.  
  833. Example 5
  834. ---------
  835. This example illustrates the use of the auxilliary functions:
  836. ADr_save(), ADr_abort(), ADr_varid(), ADr_varget(), and
  837. ADr_varput().  Cut and paste this example to EXAMPLE.PRG.
  838.  
  839. #include "read.ch"
  840. #include "inkey.ch"
  841.  
  842. //---------
  843. func main()
  844. field dbfname, shared, rlocked, date, comments
  845. local xdbfname, xshared, xrlocked, xdate, xcomments
  846. local aScn, aScn2, nT := 14, nL := 10, nB := 20, nR := 69, getlist[0]
  847. local aReadConfig := ADr_defaults(), aReadRetval
  848. local cColor := setcolor( if( iscolor(), "W+/BG, GR+/R,,, B/BG", ) )
  849.  
  850. use demo
  851. cls
  852. Xinstructions()
  853.  
  854. xdbfname = dbfname
  855. xshared = shared
  856. xrlocked = rlocked
  857. xdate = date
  858. xcomments = comments
  859.  
  860. aScn = ADbox( nT, nL, nB, nR )
  861. @nT+0, nL+2 say "[Save][Abort][Forward][Backward]";
  862.             color if( iscolor(), "R/BG", "W+/N" )
  863. @nT+1, nL+2 say "Database name         " adget xdbfname picture "@!"
  864. @nT+2, nL+2 say "Opened in shared mode?" adget xshared
  865. @nT+3, nL+2 say "Locked record number  " adget xrlocked 
  866. @nT+4, nL+2 say "Date locked           " adget xdate
  867. @nT+5, nL+2 say "Comments              " adget xcomments picture "@S33"
  868. aReadConfig[R_KEYS] = { K_ALT_S, K_ALT_A, K_ALT_F, K_ALT_B, K_F10 }
  869. aReadConfig[R_KHANDLER] = {|e,c,p,nth,nk| Xkhandler(nk)}
  870. aReadConfig[R_LBUTTONS] = {;
  871.                             { nT, nL+2, nT, nL+7 },;    // [Save]
  872.                             { nT, nL+8, nT, nL+14 },;   // [Abort]
  873.                             { nT, nL+15, nT, nL+23 },;  // [Forward]
  874.                             { nT, nL+24, nT, nL+33 };   // [Backward]
  875.                           }
  876. aReadConfig[R_LBHANDLER] = {|e,c,p,nth,mr,mc| Xlbhandler(nth)}
  877. aReadRetval = ADread( getlist, aReadConfig )
  878.  
  879. if aReadRetval[2] != K_ESC .and. aReadRetval[2] != RX_ABORT;  // IF ADread() was not aborted
  880. .and. ascan( aReadRetval[1], .T. ) > 0                        // .AND. at least one of the Gets was updated   
  881.     dbfname = xdbfname                                        // THEN update the database
  882.     shared = xshared
  883.     rlocked = xrlocked
  884.     date = xdate
  885.     comments = xcomments
  886. endif
  887.  
  888. ? ""
  889. ? "Exit Code: ", aReadRetval[2]
  890. ? " ┌─────── Updated?"
  891. ? " │     ┌─ Updated Values"
  892. ? "      "
  893. ? aReadRetval[1][1], dbfname
  894. ? aReadRetval[1][2], shared
  895. ? aReadRetval[1][3], rlocked
  896. ? aReadRetval[1][4], date
  897. ? aReadRetval[1][5], comments
  898. setcolor( cColor )
  899. return nil
  900.  
  901. //--------------------
  902. func Xkhandler( nKey )
  903. local lRetval := .F.
  904.  
  905. if nKey = K_ALT_F
  906.     Xforward()
  907. elseif nKey = K_ALT_B
  908.     Xbackward()
  909. elseif nKey = K_ALT_A
  910.     lRetval = ADr_abort()
  911. elseif nKey = K_ALT_S
  912.     lRetval = ADr_save()
  913. elseif nKey = K_F10 .and. file( "example.prg" )
  914.     save screen
  915.     memoedit( memoread( "example.prg" ), 0,0,24,79, .F. )
  916.     restore screen
  917. endif
  918.  
  919. return lRetval
  920.  
  921. //------------------------
  922. func Xlbhandler( nthSpot )
  923. local lRetval := .F.
  924.  
  925. if nthSpot = 1
  926.     lRetval = ADr_save()
  927. elseif nthSpot = 2
  928.     lRetval = ADr_abort()
  929. elseif nthSpot = 3
  930.     Xforward()
  931.     inkey(.2)
  932. elseif nthSpot = 4
  933.     Xbackward()
  934.     inkey(.2)
  935. endif
  936.  
  937. return lRetval
  938.  
  939. //-------------
  940. func Xforward()
  941. local cVarid := ADr_varid()
  942. local xCurrentValue := ADr_varget()
  943. local xNewValue, nSpacePosition, nLen
  944.  
  945. if cVarid == "XDBFNAME"
  946.     xNewValue = substr( xCurrentValue, 2 ) + left( xCurrentValue, 1 )
  947. elseif cVarid == "XSHARED"
  948.     xNewValue = if( xCurrentValue == "T", .F., .T. )
  949. elseif cVarid == "XRLOCKED"
  950.     xNewValue = val( xCurrentValue ) + 1
  951. elseif cVarid == "XDATE"
  952.     xNewValue = ctod( xCurrentValue ) + 1
  953. elseif cVarid == "XCOMMENTS"
  954.     nLen = len( xCurrentValue )
  955.     xCurrentValue = trim( xCurrentValue )
  956.     nSpacePosition = at( " ", xCurrentValue )
  957.     xNewValue = substr( xCurrentValue, nSpacePosition + 1 ) + " " + left( xCurrentValue, nSpacePosition - 1 )
  958.     xNewValue = padr( xNewValue, nLen )
  959. endif
  960.  
  961. ADr_varput( xNewValue )
  962. return nil
  963.  
  964. //-------------
  965. func Xbackward()
  966. local cVarid := ADr_varid()
  967. local xCurrentValue := ADr_varget()
  968. local xNewValue, nSpacePosition, nLen
  969.  
  970. if cVarid == "XDBFNAME"
  971.     nLen = len( xCurrentValue )
  972.     xNewValue = substr( xCurrentValue, nLen ) + left( xCurrentValue, nLen - 1 )
  973. elseif cVarid == "XSHARED"
  974.     xNewValue = if( xCurrentValue == "T", .F., .T. )
  975. elseif cVarid == "XRLOCKED"
  976.     xNewValue = val( xCurrentValue ) - 1
  977. elseif cVarid == "XDATE"
  978.     xNewValue = ctod( xCurrentValue ) - 1
  979. elseif cVarid == "XCOMMENTS"
  980.     nLen = len( xCurrentValue )
  981.     xCurrentValue = trim( xCurrentValue )
  982.     nSpacePosition = rat( " ", xCurrentValue )
  983.     xNewValue = substr( xCurrentValue, nSpacePosition + 1 ) + " " + left( xCurrentValue, nSpacePosition - 1 )
  984.     xNewValue = padr( xNewValue, nLen )
  985. endif
  986.  
  987. ADr_varput( xNewValue )
  988. return nil
  989.  
  990. //------------------
  991. func Xinstructions()
  992. ADbox( 0,0,10,79,,, .F. )
  993. @0,28 say " ADread() Demonstration #5 "
  994. @1,2 say "This example demonstrates the auxilliary functions: ADr_varid(),"
  995. @2,2 say "ADr_varget(), ADr_varput(), ADr_save(), and ADr_abort().  Check these out:"
  996. @3,2 say "  √ Press the [F10] key to display the source code (assuming that you named"
  997. @4,2 say "    the program as EXAMPLE.PRG.)  You will find ADr_save() and ADr_abort()"
  998. @5,2 say "    called in Xkhandler() and Xlbhandler();  ADr_varid(), ADr_varget() and"
  999. @6,2 say "    ADr_varput() called in Xforward() and Xbackward()."
  1000. @7,2 say "  √ Click [Forward] or [Backward] to change the value of the current GET."
  1001. @8,2 say "    You may also press the [ALT_F] or [ALT_B] to achieve the same."
  1002. @9,2 say "  √ Click [SAVE] or [ABORT], or press [ALT_S] or [ALT_A] to save or abort."
  1003. return nil
  1004.  
  1005.  
  1006. Example 6
  1007. ---------
  1008. This example demonstrates how the [R_EXITBLOCK] attribute may
  1009. be used to validate the READ as a whole.
  1010.  
  1011. #include "read.ch"
  1012. #include "inkey.ch"
  1013.  
  1014. //---------
  1015. func main()
  1016. field dbfname, shared, rlocked, date, comments
  1017. local xdbfname, xshared, xrlocked, xdate, xcomments
  1018. local aScn, nT := 17, nL := 10, nB := 23, nR := 69, getlist[0], aReadRetval
  1019. local aReadConfig := ADr_defaults()
  1020. local cColor := setcolor( if( iscolor(), "W+/BG, GR+/R,,, B/BG", ) )
  1021.  
  1022. use demo
  1023. cls
  1024. Xinstructions()
  1025.  
  1026. xdbfname = dbfname
  1027. xshared = shared
  1028. xrlocked = rlocked
  1029. xdate = date
  1030. xcomments = comments
  1031.  
  1032. aScn = ADbox( nT, nL, nB, nR )
  1033. @nT+1, nL+2 say "Database name         " adget xdbfname picture "@!"
  1034. @nT+2, nL+2 say "Opened in shared mode?" adget xshared
  1035. @nT+3, nL+2 say "Locked record number  " adget xrlocked 
  1036. @nT+4, nL+2 say "Date locked           " adget xdate
  1037. @nT+5, nL+2 say "Comments              " adget xcomments picture "@S33"
  1038. ADr_expget( getlist, {0,0,4,50 }, "W+/B", .T. )
  1039.  
  1040. // Instructs ADread() to call Xexit() before exiting.  In this example,
  1041. // Xexit checks if the fields are properly filled.  For instance, if
  1042. // 'Locked record number' is filled, then 'Date locked' must also be
  1043. // filled.
  1044. aReadConfig[R_EXITBLOCK] = {|e,c,p| Xexit(e,c,p)}
  1045.  
  1046. aReadRetval = ADread( getlist, aReadConfig )
  1047.  
  1048. if aReadRetval[2] != K_ESC .and. aReadRetval[2] != RX_ABORT;  // IF ADread() was not aborted
  1049. .and. ascan( aReadRetval[1], .T. ) > 0                        // .AND. at least one of the Gets was updated   
  1050.     dbfname = xdbfname                                        // THEN update the database
  1051.     shared = xshared
  1052.     rlocked = xrlocked
  1053.     date = xdate
  1054.     comments = xcomments
  1055. endif
  1056.  
  1057. ? ""
  1058. ? "Exit Code: ", aReadRetval[2]
  1059. ? " ┌─────── Updated?"
  1060. ? " │     ┌─ Updated Values"
  1061. ? "      "
  1062. ? aReadRetval[1][1], dbfname
  1063. ? aReadRetval[1][2], shared
  1064. ? aReadRetval[1][3], rlocked
  1065. ? aReadRetval[1][4], date
  1066. ? aReadRetval[1][5], comments
  1067. setcolor( cColor )
  1068. return nil
  1069.  
  1070.  
  1071. //-------------------
  1072. func Xexit( e, c, p )
  1073. local lValid := EXIT_READ
  1074. #define SHARED  ADr_varget(2)
  1075. #define RECORD  ADr_varget(3)
  1076. #define DATE    ADr_varget(4)
  1077.  
  1078. if p[PKREAD_EXITCODE] != K_ESC .and. p[PKREAD_EXITCODE] != RX_ABORT // not aborted
  1079.     if !SHARED
  1080.         if RECORD > 0 .or. !empty( DATE )
  1081.             Xmessage(1)
  1082.             lValid = !EXIT_READ
  1083.         endif
  1084.     else
  1085.         if RECORD = 0 .or. empty( DATE )
  1086.             Xmessage(2)
  1087.             lValid = !EXIT_READ
  1088.         elseif RECORD < 0
  1089.             Xmessage(3)
  1090.             lValid = !EXIT_READ
  1091.         endif
  1092.     endif
  1093. endif
  1094.  
  1095. return lValid
  1096.  
  1097.  
  1098. //-------------------
  1099. func Xmessage( nMsg )
  1100. local cColor := if( iscolor(), "B/W", "W+/N" )
  1101. local nCursor := setcursor(0), nRow := row(),  nCol := col()
  1102. local aScn := ADbox( 10, 9, 12, 70, cColor )
  1103.  
  1104. if nMsg = 1
  1105.     @11,11 say "Both 'Record number' and 'Date locked' must be empty" color cColor
  1106. elseif nMsg = 2
  1107.     @11,11 say "Both 'Record number' and 'Date locked' must be filled" color cColor
  1108. elseif nMsg = 3
  1109.     @11,11 say "'Record number' may not be less than 0" color cColor
  1110. endif
  1111.  
  1112. ADywait()
  1113. ADm_rwait()
  1114. ADrestscn( aScn )
  1115. setpos( nRow, nCol )
  1116. setcursor( nCursor )
  1117. return nil
  1118.  
  1119. //------------------
  1120. func Xinstructions()
  1121. ADbox( 0,0,5,79,,,.F. )
  1122. @0,28 say " ADread() Demonstration #6 "
  1123. @1,2 say "This example demonstrates how to validate the READ as a whole.  Oftentime,"
  1124. @2,2 say "you need to do this when the Gets are logically related.  In this example,"
  1125. @3,2 say "for instance, if 'Shared' is TRUE, then if either 'Record' or 'Date' is"
  1126. @4,2 say "filled, then the other must likewise be filled."
  1127. return nil
  1128.