home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 9 / 09.iso / l / l196 / 3.ddi / BOOKLOOK.BA$ / BOOKLOOK.bin
Encoding:
Text File  |  1990-06-24  |  39.0 KB  |  855 lines

  1. '****************************** Main  Module *******************************
  2. '* This window contains the module-level code of BOOKLOOK.BAS, a program   *
  3. '* used to manage the database of a hypothethical library (BOOKS.MDB). The *
  4. '* program requires the following additional modules: BOOKMOD1.BAS,        *
  5. '* BOOKMOD2.BAS, and BOOKMOD3.BAS, all named in the file BOOKLOOK.MAK. The *
  6. '* include file BOOKLOOK.BI and the database file BOOKS.MDB must also be   *
  7. '* accessible. The program is discussed in Chapter 10, Database Programming*
  8. '* with ISAM in the BASIC Programmer's Guide.                   *
  9. '*                                                                         *
  10. '* If you do NOT have expanded memory available, you should have invoked   *
  11. '* the PROISAM.EXE TSR as PROISAM /Ib:n, where n can be between 10-20.     *
  12. '* The /Ib: option specifies the number of buffers ISAM needs. Higher n    *
  13. '* values improve performance. Too few buffers, and the program will fail  *
  14. '* with an "Insufficient ISAM buffers" error. If /Ib: is set too high,     *
  15. '* there may not be enough memory to load and run the program. If you have *
  16. '* expanded memory, ISAM automatically uses up to 1.2 megabytes, even if   *
  17. '* you set Ib: to a low value. With a program the size of BOOKLOOK, use the*
  18. '* /Ie: option to reserve some expanded memory for QBX. This indirectly    *
  19. '* limits the amount of expanded memory ISAM uses, but make sure ISAM gets *
  20. '* enough EMS for at least 15 buffers (each buffer = 2K). As a last resort,*
  21. '* you can start QBX with the /NOF switch to make more memory available.   *
  22. '*                                                                         *
  23. '* BOOKLOOK manages 3 tables, BookStock, CardHolders, and BooksOut. The    *
  24. '* data in the BookStock and CardHolders tables is displayed as forms on   *
  25. '* screen. The user can switch between table displays by pressing "V" (for *
  26. '* View Other Table). Each table is defined as a separate structure. The   *
  27. '* structure for BookStock is Books, for CardHolders it is Borrowers, and  *
  28. '* for BooksOut it is BookStatus. Each of these is incorporated as an      *
  29. '* element of the structure RecStruct. RecStruct also has an element of    *
  30. '* INTEGER type called TableNum (to keep track of which table is being     *
  31. '* displayed), and a STRING element called WhichIndex that holds the name  *
  32. '* of the index by which the user chooses to order presentation of records.*
  33. '* Press F2 to see a list of procedures called by the program.             *
  34. '***************************************************************************
  35.  
  36. DEFINT A-Z
  37. '$INCLUDE: 'BOOKLOOK.BI'
  38. SCREEN 0
  39. CLS                         ' TempRec is for editing and adding records
  40. DIM TempRec AS RecStruct    ' Used only to blank out a TempRec
  41. DIM EmptyRec AS RecStruct   ' See BOOKLOOK.BI for declaration of
  42. DIM BigRec AS RecStruct     ' this structure and its elements
  43. DIM Marker(25) AS INTEGER   ' Array to hold SAVEPOINT returns
  44.  
  45. ' Open the database and the BookStock, CardHolders, and BooksOut tables
  46.  
  47. ON ERROR GOTO MainHandler
  48. OPEN "BOOKS.MDB" FOR ISAM Books "BookStock" AS cBookStockTableNum
  49. OPEN "BOOKS.MDB" FOR ISAM Borrowers "CardHolders" AS cCardHoldersTableNum
  50. OPEN "BOOKS.MDB" FOR ISAM BookStatus "BooksOut" AS cBooksOutTableNum
  51. ON ERROR GOTO 0
  52.  
  53. BigRec.TableNum = cBookStockTableNum   ' Decide which table to show first
  54.  
  55.    ' Since the database has multiple tables, this outer DO loop is used to
  56.    ' reset the number associated with the table the user wants to
  57.    ' to access, then draw the screen appropriate to that table, etc.
  58. DO
  59.   EraseMessage                          ' Show the interface
  60.   CALL DrawScreen(BigRec.TableNum)
  61.   Checked = CheckIndex%(BigRec, TRUE)   ' Show current index
  62.   CALL Retriever(BigRec, DimN, DimP, answer) ' Retrieve and show a record
  63.   CALL ShowMessage(" Press V to View other table", 0)
  64.   CALL ShowStatus(" Total records in table: ", CDBL(LOF(BigRec.TableNum)))
  65.     
  66.   ' This loop lets the user traverse BigRec.TableNum and insert, delete,
  67.   ' or modify records.
  68.   DO                                           ' At start of each loop, show
  69.                                                ' the user valid operations
  70.     CALL Retriever(BigRec, DimN, DimP, answer) ' and display current record
  71.  
  72.     STACK 4000                          ' Set large stack for recursions-it
  73.                                         ' also resets FRE(-2) to stack 4000.
  74.  
  75.     answer% = GetInput%(BigRec)         ' Find out what the user wants to do
  76.  
  77.     IF answer < UNDO THEN               ' Excludes UNDOALL & INVALIDKEY too
  78.       CALL EditCheck(PendingFlag, answer, BigRec)
  79.     END IF
  80.  
  81.     SELECT CASE answer         ' Process valid user requests
  82.       CASE QUIT
  83.         CALL ShowMessage(" You chose Quit. So long! ", 0)
  84.         END
  85.  
  86.                                ' If user picks "N" (Next Record), MOVENEXT.
  87.                                ' CheckPosition handles end-of-file (i.e. the
  88.       CASE GOAHEAD, ENDK       ' position just past the last record). If EOF
  89.                                ' or BOF = TRUE, CheckPosition holds position
  90.         MOVENEXT BigRec.TableNum
  91.         CALL CheckPosition(BigRec, answer, DimN, DimP)
  92.  
  93.                                ' Same logic as GOAHEAD, but reversed
  94.       CASE GOBACK, HOME
  95.  
  96.         MOVEPREVIOUS BigRec.TableNum
  97.         CALL CheckPosition(BigRec, answer, DimN, DimP)
  98.  
  99.                                ' If user chooses "E", let him edit a field.
  100.                                ' Assign the value returned by SAVEPOINT to
  101.                                ' an array element, then update the table and
  102.                                ' show the changed field. Trap any "duplicate
  103.       CASE EDITRECORD          ' value for unique index" (error 86) and
  104.                                ' handle it. The value returned by SAVEPOINT
  105.                                ' allows rollbacks so the user can undo edits
  106.  
  107.         IF LOF(BigRec.TableNum) THEN
  108.           IF EditField(Argument%, BigRec, Letter$, EDITRECORD, answer%) THEN
  109.  
  110.               ' You save a sequence of savepoint identifiers in an array so
  111.               ' you can let the user roll the state of the file back to a
  112.               ' specific point. The returns from SAVEPOINT aren't guaranteed
  113.               ' to be sequential.
  114.             n = n + 1              ' Increment counter first so savepoint
  115.             Marker(n) = SAVEPOINT  ' is synced with array-element subscript
  116.  
  117.             Alert$ = "Setting Savepoint number " + STR$(Marker(n))
  118.             CALL ShowMessage(Alert$, 0)
  119.             ON ERROR GOTO MainHandler
  120.             SELECT CASE BigRec.TableNum   ' Update the table being displayed
  121.               CASE cBookStockTableNum
  122.                 UPDATE BigRec.TableNum, BigRec.Inventory
  123.               CASE cCardHoldersTableNum
  124.                 UPDATE BigRec.TableNum, BigRec.Lendee
  125.             END SELECT
  126.             ON ERROR GOTO 0
  127.           ELSE
  128.             COMMITTRANS               ' Use COMMITTRANS abort transaction if
  129.             PendingFlag = FALSE       ' the user presses ESC
  130.             n = 0                     ' Reset array counter
  131.           END IF
  132.         ELSE
  133.           CALL ShowMessage("Sorry, no records in this table to edit", 0): SLEEP
  134.         END IF
  135.                           ' If choice is "A", get the values the user wants
  136.                           ' in each of the fields (with AddOne). If there
  137.                           ' is no ESCAPE from the edit, INSERT the record.
  138.                           ' Trap "Duplicate value for unique index" errors
  139.                           ' and handle them in MainHandler (error 86).
  140.       CASE ADDRECORD
  141.         added = AddOne(BigRec, EmptyRec, TempRec, answer%)
  142.         IF added THEN
  143.           Alert$ = "A new record assumes proper place in current index"
  144.           CALL ShowMessage(Alert$, 0)
  145.           ON ERROR GOTO MainHandler
  146.           SELECT CASE BigRec.TableNum     ' Insert into table being shown
  147.             CASE cBookStockTableNum
  148.               INSERT BigRec.TableNum, TempRec.Inventory
  149.             CASE cCardHoldersTableNum
  150.               INSERT BigRec.TableNum, TempRec.Lendee
  151.           END SELECT
  152.           ON ERROR GOTO 0
  153.         END IF
  154.         TempRec = EmptyRec
  155.         
  156.                               ' If choice is "D" --- prompt for confirmation.
  157.                               ' If so, delete it and show new current record.
  158.       CASE TOSSRECORD
  159.         AnyRecords = LOF(BigRec.TableNum)
  160.         IF BigRec.TableNum = cBookStockTableNum THEN CheckedOut = GetStatus(BigRec, 0#)
  161.         IF BigRec.TableNum = cCardHoldersTableNum THEN
  162.           SETINDEX cBooksOutTableNum, "CardNumIndexBO"
  163.           SEEKEQ cBooksOutTableNum, BigRec.Lendee.CardNum
  164.           IF NOT EOF(cBooksOutTableNum) THEN CheckedOut = TRUE
  165.         END IF
  166.         IF AnyRecords AND CheckedOut = FALSE THEN
  167.           Alert$ = "Press D again to Delete this record, ESC to escape"
  168.           CALL ShowMessage(Alert$, 0)
  169.           DeleteIt% = GetInput%(BigRec)
  170.           IF DeleteIt% = TOSSRECORD THEN   ' Delete currently-displayed record
  171.             DELETE BigRec.TableNum
  172.             CALL ShowMessage("Record deleted...Press a key to continue", 0)
  173.           ELSE
  174.             CALL ShowMessage("Record not deleted. Press a key to continue", 0)
  175.             CALL ShowRecord(BigRec)
  176.           END IF
  177.           ' The following code checks whether the record deleted was the last
  178.           ' record in the index, then makes the new last record current
  179.           IF EOF(BigRec.TableNum) THEN
  180.             MOVELAST BigRec.TableNum
  181.           END IF
  182.         ELSE
  183.           IF BigRec.TableNum = cBookStockTableNum THEN
  184.             IF AnyRecords = 0 THEN Alert$ = "Sorry, no records in this table to delete"
  185.             IF CheckedOut THEN Alert$ = "Can't delete --- this book currently checked out!"
  186.           ELSEIF BigRec.TableNum = cCardHoldersTableNum THEN
  187.             IF AnyRecords = 0 THEN Alert$ = "Sorry, no records in this table to delete"
  188.             IF CheckedOut THEN Alert$ = "Can't delete --- this cardholder still has books out!"
  189.           END IF
  190.           CALL ShowMessage(Alert$, 0): SLEEP
  191.         END IF
  192.         CheckedOut = FALSE
  193.  
  194.                                ' If user chooses "R", walk the fields so he
  195.                                ' can choose new index to order presentation
  196.       CASE REORDER
  197.         Letter$ = CHR$(TABKEY)
  198.         GotOne = ChooseOrder(BigRec, EmptyRec, TempRec, Letter$, REORDER)
  199.  
  200.                                ' If a choice of indexes was made, retrieve
  201.                                ' the index name, set an error trap, and try
  202.                                ' to set the index, then display new index.
  203.         IF GotOne THEN
  204.           IndexName$ = LTRIM$(RTRIM$(TempRec.WhichIndex))
  205.           ON ERROR GOTO MainHandler
  206.           IF IndexName$ <> "NULL" THEN            ' This string is placed in
  207.             SETINDEX BigRec.TableNum, IndexName$  ' TempRec.WhichIndex if
  208.           ELSE                                    ' user chooses "Default."
  209.             SETINDEX BigRec.TableNum, ""          ' "" is valid index name
  210.           END IF                                  'representing NULL index
  211.           ON ERROR GOTO 0                         '(i.e. the default order)
  212.           CALL AdjustIndex(BigRec)
  213.           LSET TempRec = EmptyRec
  214.         END IF
  215.  
  216.                           ' If choice is "F", first set current index
  217.       CASE SEEKFIELD      ' using same procedure as REORDER. Then do seek.
  218.  
  219.         Letter$ = CHR$(TABKEY)        ' Pass TABKEY for PlaceCursor
  220.         GotOne = ChooseOrder(BigRec, EmptyRec, TempRec, Letter$, SEEKFIELD)
  221.  
  222.         IF GotOne AND TEXTCOMP(TempRec.WhichIndex, "NULL") THEN
  223.           CALL SeekRecord(BigRec, TempRec, Letter$)
  224.           FirstLetter$ = ""
  225.           DimN = EOF(BigRec.TableNum): DimP = BOF(BigRec.TableNum)
  226.         END IF
  227.  
  228.                           ' STATUS gets the due date of a book & displays it
  229.       CASE STATUS
  230.         IF BigRec.TableNum = cBookStockTableNum THEN
  231.           CALL ShowStatus("", 0#)                  ' Explicitly type the 0
  232.           GotIt = GetStatus(BigRec, DateToShow#)   ' to avoid type mismatch
  233.           IF GotIt THEN
  234.             Alert$ = "Press B for information on Borrower of this book"
  235.             CALL ShowMessage(Alert$, 0)
  236.             CALL ShowStatus("Due Date: ", DateToShow#)
  237.           END IF
  238.         END IF
  239.  
  240.                          ' LendeeProfile displays borrower of displayed book
  241.       CASE BORROWER
  242.         CALL LendeeProfile(BigRec)
  243.  
  244.                          ' BooksBorrowed shows books borrowed by CardHolder
  245.       CASE WHICHBOOKS
  246.         IF Borrowed THEN CALL BooksBorrowed(BigRec)
  247.  
  248.                          ' If user hits "V" cycle through displayable tables
  249.       CASE OTHERTABLE
  250.         IF BigRec.TableNum < cDisplayedTables THEN
  251.           BigRec.TableNum = BigRec.TableNum + 1
  252.         ELSE
  253.           BigRec.TableNum = 1
  254.         END IF
  255.         EXIT DO
  256.                          ' If user picks "I" to check current book back in,
  257.                          ' make sure it is out, then check it back in
  258.       CASE CHECKIN
  259.         IF Borrowed THEN
  260.           GotIt = GetStatus(BigRec, DateToShow#)
  261.           IF DateToShow# THEN
  262.             CALL ReturnBook(BigRec, DateToShow#)
  263.           END IF
  264.         END IF
  265.                          ' If user picks "O" to check current book out,
  266.                          ' make sure it is available, then check it out
  267.       CASE CHECKOUT
  268.         GotIt = GetStatus(BigRec, DateToShow#)
  269.           IF DateToShow# = 0# THEN
  270.              CALL BorrowBook(BigRec)
  271.           ELSE
  272.              CALL ShowMessage("Sorry, this book is already checked out...", 0)
  273.           END IF
  274.  
  275.                         ' If user wants to Undo all or some of a series of
  276.                         ' uncommitted edits, make sure there is a pending
  277.                         ' transaction to undo, then restore the state of the
  278.                         ' file one step at a time, or altogether, depending
  279.                         ' on whether U or ^U was entered.
  280.       CASE UNDO, UNDOALL
  281.         IF PendingFlag = TRUE THEN
  282.           IF n < 1 THEN
  283.             CALL ShowMessage("No pending edits left to Undo...", 0)
  284.           ELSE
  285.             IF answer = UNDO THEN
  286.               Alert$ = "Restoring back to Savepoint # " + STR$(Marker(n))
  287.               CALL ShowMessage(Alert$, 0)
  288.               ROLLBACK Marker(n)
  289.               n = n - 1
  290.             ELSE                    ' If it's not UNDO, it must be UNDOALL
  291.               CALL ShowMessage("Undoing the whole last series of edits", 0)
  292.               ROLLBACK ALL
  293.               n = 0
  294.             END IF
  295.          END IF
  296.        ELSE
  297.          CALL ShowMessage("There are no pending edits left to Undo...", 0)
  298.        END IF
  299.  
  300.       CASE INVALIDKEY              ' Alert user if wrong key is pressed
  301.         CALL ShowMessage(KEYSMESSAGE, 0)
  302.         IF PendingFlag = TRUE THEN CALL DrawIndexBox(BigRec.TableNum, EDITRECORD)
  303.     END SELECT
  304.     CALL DrawHelpKeys(BigRec.TableNum)
  305.     CALL ShowKeys(BigRec, BRIGHT + FOREGROUND, DimN, DimP)
  306.   LOOP
  307. LOOP
  308. CLOSE
  309. END
  310.  
  311. ' This error handler takes care of the most common ISAM errors
  312.  
  313. MainHandler:
  314.  
  315. IF ERR = 73 THEN        ' 73 = Feature unavailable
  316.   CALL ShowMessage("You forgot to load the ISAM TSR program", 0)
  317.   END
  318. ELSEIF ERR = 88 THEN        ' 88 = Database inconsistent
  319.   ' If you have text files corresponding to each of the tables, then
  320.   ' MakeOver prompts for their names and creates an ISAM file from them.
  321.   CALL MakeOver(BigRec)
  322.   RESUME NEXT
  323.  
  324. ELSEIF ERR = 83 THEN        ' 83 = Index not found
  325.     CALL DrawScreen(BigRec.TableNum)
  326.     CALL ShowMessage("Unable to set the index. Need more buffers?", 0)
  327.     RESUME NEXT
  328. ELSEIF ERR = 86 THEN        ' 86 = Duplicate value for unique index
  329.     ' Trap errors when a user tries to enter a value for the Card Number or
  330.     ' ID fields that duplicates a value already in the table
  331.     IF answer = ADDRECORD THEN CALL DupeFixer(TempRec) ELSE CALL DupeFixer(BigRec)
  332.     RESUME
  333. ELSE
  334.   Alert$ = "Sorry, not able to handle this error in BOOKLOOK: " + STR$(ERR)
  335.   CALL ShowMessage(Alert$, 0)
  336.   END
  337. END IF
  338.  
  339. '***************************************************************************
  340. '*  The AddOne FUNCTION is called once for each field when the user wants  *
  341. '*  to add a record to the displayed table.                                *
  342. '*                                Parameters                               *
  343. '*  BigRec    RecStruct variable containing information on all tables      *
  344. '*  EmptyRec  Empty record of same type as BigRec                          *
  345. '*  TempRec   Temporary record record of same type as BigRec               *
  346. '*  Answer    Integer passed through to EditField; tells task to perform   *
  347. '***************************************************************************
  348. FUNCTION AddOne (BigRec AS RecStruct, EmptyRec AS RecStruct, TempRec AS RecStruct, answer%)
  349.   CALL ClearEm(BigRec.TableNum, 1, 1, 1, 1, 1, 1)
  350.   LSET TempRec = EmptyRec
  351.   CALL ShowMessage("Enter the first field of the new record", 0)
  352.   TempRec.TableNum = BigRec.TableNum
  353.   Edited = EditField(Argument%, TempRec, FirstLetter$, ADDRECORD, answer%)
  354.   IF Edited THEN
  355.     AddOne = -1
  356.   ELSE
  357.     AddOne = 0
  358.   END IF
  359.   COLOR FOREGROUND, BACKGROUND
  360. END FUNCTION
  361.  
  362. '***************************************************************************
  363. '* The CheckPosition SUB checks the table position after the requested user*
  364. '* action is completed. If EOF follows a MOVENEXT or the user has chosen   *
  365. '* MOVELAST, the Keys for Database Viewing/Editing box is updated to say   *
  366. '* "No Next Record." If BOF  follows a MOVEPREVIOUS or user has chosen a   *
  367. '* MOVEFIRST, "No Previous Record" is displayed.                           *
  368. '* In either case, the position is held by executing MOVELAST or MOVEFIRST.*
  369. '*                            Parameters:                                  *
  370. '*   Big Rec      User-defined type containing all table information       *
  371. '*   Answer       Tells what operation retrieve results from               *
  372. '*   DimN & DimP  Flags telling which menu items should be dimmed/changed  *
  373. '***************************************************************************
  374. SUB CheckPosition (BigRec AS RecStruct, answer, DimN%, DimP%)
  375.   SELECT CASE answer
  376.     CASE GOAHEAD, ENDK
  377.       IF EOF(BigRec.TableNum) OR (answer = ENDK) THEN
  378.         CALL ShowMessage("This is the last record in this index", 0)
  379.         DimN = TRUE: DimP = FALSE
  380.         MOVELAST BigRec.TableNum
  381.       ELSE                    ' If not EOF, turn on N
  382.         DimN = FALSE: DimP = FALSE
  383.         CALL EraseMessage
  384.       END IF
  385.     CASE GOBACK, HOME
  386.       IF BOF(BigRec.TableNum) OR (answer = HOME) THEN
  387.         CALL ShowMessage("This is the first record in this index", 0)
  388.         DimP = TRUE: DimN = FALSE
  389.         MOVEFIRST BigRec.TableNum
  390.       ELSE
  391.         DimP = FALSE: DimN = FALSE
  392.         CALL EraseMessage
  393.       END IF
  394.   END SELECT
  395. END SUB
  396.  
  397. '***************************************************************************
  398. '* The ChooseOrder FUNCTION calls PlaceCursor so the user can move around  *
  399. '* the form to pick the index to set.                                      *
  400. '*                                  Parameters                             *
  401. '*  BigRec       BigRec has all the table information in updated form      *
  402. '*  EmptyRec     EmptyRec is same template as BigRec, but fields are empty *
  403. '*  TempRec      Holds intermediate and temporary data                     *
  404. '*  FirstLetter  Catches letter if user starts typing during SEEKFIELD     *
  405. '*  Task         Either REORDER or SEEKFIELD - passed on to PlaceCursor    *
  406. '***************************************************************************
  407. FUNCTION ChooseOrder (BigRec AS RecStruct, EmptyRec AS RecStruct, TempRec AS RecStruct, FirstLetter$, Task%)
  408.   CALL DrawTable(BigRec.TableNum)
  409.   CALL DrawIndexBox(BigRec.TableNum, Task)
  410.   Argument = TITLEFIELD                    ' Always start with first field
  411.   TempRec = EmptyRec: TempRec.TableNum = BigRec.TableNum
  412.  
  413.   ' Pass temporary RecStruct variable so user can't trash BigRec
  414.   value = PlaceCursor(Argument, TempRec, FirstLetter$, 1, Task)
  415.  
  416.   ' If the user chooses ESC, redraw everything, then exit to module level
  417.   IF ASC(TempRec.WhichIndex) = 0 THEN
  418.     CALL DrawIndexBox(BigRec.TableNum, Task)
  419.     CALL ShowRecord(BigRec)
  420.     CALL ShowMessage(KEYSMESSAGE, 0)
  421.     ChooseOrder = 0
  422.     EXIT FUNCTION
  423.   ELSE                                ' Otherwise, if user makes a choice
  424.     ChooseOrder = -1                  ' of Indexes, signal success to the
  425.   END IF                              ' module-level code
  426. END FUNCTION
  427.  
  428. '***************************************************************************
  429. '*                                                                         *
  430. '*  The DupeFixer SUB is called when the tries to enter a duplicate value  *
  431. '*  for the BookStock table's IDnum column or the the CardHolders table's  *
  432. '*  CardNum column, because their indexes are Unique. The procedure prompts*
  433. '*  the user to enter a new value.                                         *
  434. '***************************************************************************
  435. SUB DupeFixer (BigRec AS RecStruct)
  436.     IF BigRec.TableNum = cBookStockTableNum THEN
  437.       DO
  438.         Alert$ = STR$(BigRec.Inventory.IDnum) + " is not unique. "
  439.         CALL ShowMessage(Alert$, 1)
  440.         COLOR YELLOW + BRIGHT, BACKGROUND
  441.         INPUT "Try another number: ", TempString$
  442.         BigRec.Inventory.IDnum = VAL(TempString$)
  443.       LOOP UNTIL BigRec.Inventory.IDnum
  444.     ELSEIF BigRec.TableNum = cCardHoldersTableNum THEN
  445.       DO
  446.         Alert$ = STR$(BigRec.Lendee.CardNum) + " is not unique. "
  447.         CALL ShowMessage(Alert$, 1)
  448.         COLOR YELLOW + BRIGHT, BACKGROUND
  449.         INPUT "Try another number: ", TempString$
  450.         BigRec.Lendee.CardNum = VAL(TempString$)
  451.       LOOP UNTIL BigRec.Lendee.CardNum
  452.     END IF
  453.     COLOR FOREGROUND, BACKGROUND
  454. END SUB
  455.  
  456. '********************************* EditCheck SUB ***************************
  457. '*                                                                         *
  458. '* The EditCheck procedure monitors what the user wants to do, and if the  *
  459. '* choice is EDITRECORD, makes sure that a transaction is begun, or if it  *
  460. '* already has begun, continues it. If a transaction has been pending, and *
  461. '* the user chooses anything except EDITRECORD, then the transaction is    *
  462. '* committed.                                                              *
  463. '*                                                                         *
  464. '*                            Parameters:                                  *
  465. '*   Pending      A flag that indicates whether transaction is pending     *
  466. '*   Task         Tells what operation the user wants to perform now       *
  467. '*   TablesRec    Structure containing information about the tables        *
  468. '*                                                                         *
  469. '***************************************************************************
  470. SUB EditCheck (Pending, Task, TablesRec AS RecStruct)
  471.   ' First, decide if this is a new or pending transaction, or not one at all
  472.   ' The only transaction in this program keeps edits to the current record
  473.   ' pending until the user moves on to a new record or a new operation
  474.   ' (for example a Reorder).
  475. SHARED n                          ' n is index to array of savepoint ids
  476.  
  477.       IF Task = EDITRECORD THEN
  478.         IF Pending = FALSE THEN
  479.           BEGINTRANS
  480.           Pending = TRUE
  481.         END IF
  482.       ELSEIF Pending = TRUE THEN  ' Equivalent to Task<>EDITRECORD AND
  483.         COMMITTRANS               ' Pending=TRUE
  484.         Pending = FALSE
  485.         n = 0                     ' Reset array index for savepoint ids
  486.         CALL DrawIndexBox(TablesRec.TableNum, 0)
  487.       END IF
  488. END SUB
  489.  
  490. '***************************************************************************
  491. '*  The GetInput FUNCTION takes the keystroke input by the user and returns*
  492. '*  a constant indicating what the user wants to do. If the keystroke rep- *
  493. '*  resents a valid operation, the choice is echoed to the screen.         *
  494. '***************************************************************************
  495. FUNCTION GetInput% (BigRec AS RecStruct)
  496. DO
  497.   answer$ = INKEY$
  498. LOOP WHILE answer$ = EMPTYSTRING
  499.   IF LEN(answer$) > 1 THEN
  500.     RightSide = HighKeys%(answer$)
  501.     GetInput = RightSide
  502.   ELSE
  503.     SELECT CASE answer$
  504.       CASE "A", "a"
  505.         CALL UserChoice(BigRec, ALINE, 7, "Add Record")
  506.         GetInput% = ADDRECORD
  507.       CASE "B", "b"
  508.         IF BigRec.TableNum = cBookStockTableNum THEN
  509.           CALL UserChoice(BigRec, WLINE, 28, "Borrower")
  510.           GetInput% = BORROWER
  511.         ELSE
  512.           CALL UserChoice(BigRec, WLINE, 13, "Books Outstanding")
  513.           GetInput% = WHICHBOOKS
  514.         END IF
  515.       CASE "O", "o"
  516.         CALL UserChoice(BigRec, CLINE, 7, "Check Book Out")
  517.         GetInput% = CHECKOUT
  518.       CASE "I", "i"
  519.         CALL UserChoice(BigRec, CLINE, 28, "Check In")
  520.         GetInput% = CHECKIN
  521.       CASE "D", "d"
  522.         CALL UserChoice(BigRec, ALINE, 28, "Drop Record")
  523.         GetInput% = TOSSRECORD
  524.       CASE "N", "n"
  525.         GetInput% = GOAHEAD
  526.       CASE "P", "p"
  527.         GetInput% = GOBACK
  528.       CASE "Q", "q"
  529.         CALL UserChoice(BigRec, ELINE, 28, "Quit")
  530.         GetInput% = QUIT
  531.       CASE "E", "e"
  532.         CALL UserChoice(BigRec, ELINE, 7, "Edit Record")
  533.         GetInput% = EDITRECORD
  534.       CASE "F", "f"
  535.         CALL UserChoice(BigRec, RLINE, 28, "Find Record")
  536.         GetInput% = SEEKFIELD
  537.       CASE "R", "r"
  538.         CALL UserChoice(BigRec, RLINE, 7, "Reorder Records")
  539.         GetInput% = REORDER
  540.       CASE "V", "v"
  541.         GetInput% = OTHERTABLE
  542.       CASE "W", "w"
  543.         CALL UserChoice(BigRec, WLINE, 7, "When Due Back")
  544.         GetInput% = STATUS
  545.       CASE CHR$(ESCAPE)
  546.         GetInput% = ESCAPE
  547.       CASE "U", "u"
  548.         GetInput = UNDO       ' U signals rollback request after editing
  549.       CASE CHR$(CTRLU)        ' ^U = rollback a whole series of edits
  550.         GetInput = UNDOALL
  551.       CASE ELSE
  552.         GetInput% = INVALIDKEY
  553.         BEEP
  554.     END SELECT
  555.   END IF
  556. END FUNCTION
  557.  
  558. '**************************************************************************
  559. '*  The HighKeys FUNCTION handles common two-byte keys input by the user. *
  560. '*  The Answer parameter is the keystroke entered by the user.            *                                                          *
  561. '**************************************************************************
  562. FUNCTION HighKeys (answer AS STRING)
  563.   SELECT CASE ASC(RIGHT$(answer$, 1))     ' Look at code for right byte
  564.     CASE UP
  565.       HighKeys = GOBACK                   ' UP is the up-arrow key
  566.     CASE DOWN
  567.       HighKeys = GOAHEAD                  ' DOWN is the down-arrow key
  568.     CASE HOME
  569.       HighKeys = HOME                     ' etc.
  570.     CASE ENDK
  571.       HighKeys = ENDK
  572.       CASE LEFT
  573.       HighKeys = OTHERTABLE
  574.     CASE RIGHT
  575.       HighKeys = OTHERTABLE
  576.     CASE PGUP
  577.       CALL ShowMessage("You could program so PGUP moves back n records", 0): SLEEP
  578.       HighKeys = INVALIDKEY
  579.     CASE PGDN
  580.       CALL ShowMessage("You could program so PGDN moves forward n records", 0): SLEEP
  581.       HighKeys = INVALIDKEY
  582.     CASE ELSE
  583.       CALL ShowMessage("Sorry, that key isn't handled yet.", 0): SLEEP
  584.       HighKeys = INVALIDKEY
  585.   END SELECT
  586. END FUNCTION
  587.  
  588. '****************************** Retriever SUB ******************************
  589. '* The Retriever SUB retrieves records from the database file and puts     *
  590. '* them into the appropriate recordvariable for the table being displayed. *
  591. '* An error trap is set in case the retrieve fails, in which case a message*
  592. '* is displayed. Note that if a preceding SEEKoperand fails, EOF is TRUE.  *
  593. '* In that case, position is set to the last record, which is retrieved.   *
  594. '*                            Parameters:                                  *
  595. '*   Big Rec      User-defined type containing all table information       *
  596. '*   DimN & DimP  Flags telling which menu items should be dimmed/changed  *
  597. '*   Task         Tells what operation retrieve results from               *
  598. '***************************************************************************
  599. SUB Retriever (BigRec AS RecStruct, DimN, DimP, Task)
  600.   STATIC PeekFlag         ' Set this if user is just peeking at other table
  601.   LOCATE , , 0            ' Turn off the cursor
  602.   ' Show the user which choice was made, and whether EOF or BOF
  603.   CALL ShowKeys(BigRec, FOREGROUND + BRIGHT, DimN, DimP)
  604.   ' If table is empty, don't try to retrieve anything
  605.   IF LOF(BigRec.TableNum) = 0 THEN
  606.     DrawTable (BigRec.TableNum)
  607.     CALL ShowMessage("There are no records in this table", 0): EXIT SUB
  608.   END IF
  609.  
  610.   IF Task <> ENDK AND Task <> HOME THEN
  611.     IF Task < EDITRECORD THEN                         ' Edit needs its
  612.       CALL Indexbox(BigRec, CheckIndex%(BigRec, 0))   ' own prompts. Show
  613.     ELSEIF Task > INVALIDKEY THEN                     ' indexbox otherwise
  614.       IF Task <> ESC THEN CALL DrawIndexBox(BigRec.TableNum, 0)
  615.       CALL Indexbox(BigRec, CheckIndex%(BigRec, 0))
  616.     END IF
  617.   END IF
  618.   IF BOF(BigRec.TableNum) THEN MOVEFIRST (BigRec.TableNum)
  619.   ON LOCAL ERROR GOTO LocalHandler           ' Trap errors on the retrieve.
  620.   IF NOT EOF(BigRec.TableNum) THEN           ' Retrieve current record
  621.     SELECT CASE BigRec.TableNum              ' from table being displayed
  622.       CASE cBookStockTableNum                ' if EOF is not true
  623.         RETRIEVE BigRec.TableNum, BigRec.Inventory
  624.       CASE cCardHoldersTableNum
  625.         RETRIEVE BigRec.TableNum, BigRec.Lendee
  626.     END SELECT
  627.   ELSE                                       ' If EOF is true, set position
  628.     MOVELAST BigRec.TableNum                 ' to the last record in table,
  629.     SELECT CASE BigRec.TableNum              ' then retrieve the record
  630.       CASE cBookStockTableNum
  631.         RETRIEVE BigRec.TableNum, BigRec.Inventory
  632.       CASE cCardHoldersTableNum
  633.         RETRIEVE BigRec.TableNum, BigRec.Lendee
  634.     END SELECT
  635.     DimN = TRUE
  636.   END IF
  637.   ON LOCAL ERROR GOTO 0                             ' Turn off error trap
  638.   CALL ClearEm(BigRec.TableNum, 1, 1, 1, 1, 1, 1)
  639.   CALL ShowRecord(BigRec)
  640.   IF Task = OTHERTABLE THEN   ' If user is just peeking at the other table
  641.     IF PeekFlag = 0 THEN      ' remind him how to get back to first table
  642.       CALL ShowMessage("Press V to return to the other table", 0)
  643.       PeekFlag = 1
  644.     END IF
  645.   ELSE
  646.     PeekFlag = 0
  647.   END IF
  648. EXIT SUB
  649.  
  650. LocalHandler:
  651.   IF ERR = 85 THEN
  652.     CALL ShowMessage("Unable to retrieve your record...", 0)
  653.   END IF
  654.   RESUME NEXT
  655. END SUB
  656.  
  657. '********************************* SeekRecord SUB *************************
  658. '*  SeekRecord takes the name of the user's chosen index, sets it as the  *
  659. '*  current index, then prompts the user to enter the value to seek. A    *
  660. '*  minimal editor, MakeString, gets user input. If the SEEK is on a com- *
  661. '*  bined index, GetKeyVals is called to get the input. Input is checked  *
  662. '*  for minimal acceptability by ValuesOK. If it is OK, GetOperand is     *
  663. '*  called to let the user specify how to conduct the SEEK.               *
  664. '*                              Parameters:                               *
  665. '*      TablesRec  Contains current record information for all tables     *
  666. '*      TempRec    Contains the name of the index on which to seek (in    *
  667. '*                 TempRec.WhichIndex element)                            *
  668. '*      Letter$    If the user starts typing instead of pressing ENTER    *
  669. '*                 Letter$ catches the keystroke, passes it to MakeString *
  670. '**************************************************************************
  671. SUB SeekRecord (TablesRec AS RecStruct, TempRec AS RecStruct, Letter$)
  672.   DIM EmptyRec AS RecStruct             ' Make an empty record.
  673.   IF LEFT$(Letter$, 1) < " " THEN       ' Exit if value is not a valid
  674.                                         ' character, then redraw
  675.     CALL DrawIndexBox(TablesRec.TableNum, SEEKFIELD)
  676.     CALL Indexbox(TablesRec, CheckIndex%(TablesRec, TRUE))
  677.     CALL ShowMessage("You must enter a valid string or numeric value", 0)
  678.     EXIT SUB
  679.   END IF
  680.   TheTable = TablesRec.TableNum
  681.   IndexName$ = RTRIM$(TempRec.WhichIndex)
  682.   IF GETINDEX$(TheTable) <> IndexName$ THEN  ' If index to seek on is not
  683.     ON LOCAL ERROR GOTO SeekHandler          ' current, set it now. Trap
  684.     SETINDEX TheTable, IndexName$            ' possible failure of SETINDEX
  685.     ON LOCAL ERROR GOTO 0                    ' then turn off error trap.
  686.   END IF
  687.   CALL AdjustIndex(TablesRec)                ' Show the current index
  688.   TablesRec.WhichIndex = TempRec.WhichIndex
  689.   TempRec = EmptyRec                         ' Clear TempRec for data
  690.   TempRec.TableNum = TablesRec.TableNum
  691.   ' Get the value to SEEK for from the user. The data type you assign the
  692.   ' input to must be the same as the data in the database, so get it as a
  693.   ' string with MakeString, then convert it to proper type for index. If
  694.   ' the index is the combined index BigIndex, use GetKeyVals for input...
  695.  
  696.  SELECT CASE RTRIM$(LTRIM$(IndexName$))
  697.    CASE "TitleIndexBS", "AuthorIndexBS", "PubIndexBS", "NameIndexCH", "StateIndexCH"
  698.     Prompt$ = "Value To Seek: "
  699.     Key1$ = MakeString$(ASC(Letter$), Prompt$): IF Key1$ = "" THEN EXIT SUB
  700.    CASE "IDIndex", "CardNumIndexCH", "ZipIndexCH"
  701.     ValueToSeek$ = MakeString$(ASC(Letter$), Prompt$)
  702.     IF ValueToSeek$ = "" THEN EXIT SUB
  703.     IF IndexName$ = "IDIndex" THEN
  704.       NumberToSeek# = VAL(ValueToSeek$)
  705.       Key1$ = ValueToSeek$
  706.     ELSE
  707.       NumberToSeek& = VAL(ValueToSeek$)
  708.       Key1$ = ValueToSeek$
  709.     END IF
  710.    CASE "BigIndex"
  711.     CALL GetKeyVals(TempRec, Key1$, Key2$, Key3#, Letter$)
  712.     ValueToSeek$ = STR$(Key3#)
  713.    CASE ""
  714.      Alert$ = "Sorry, can't search for field values on the default index"
  715.      CALL ShowMessage(Alert$, 0)
  716.    CASE ELSE
  717.   END SELECT
  718.  
  719.   ' Make sure the input values are minimally acceptable
  720.  
  721.   IF NOT ValuesOK(TablesRec, Key1$, Key2$, ValueToSeek$) THEN
  722.     CALL ShowMessage("Sorry, problem with your entry. Try again!", 0)
  723.     EXIT SUB
  724.   END IF
  725.  
  726.   ' Show the user the values he entered in their appropriate fields
  727.   CALL ClearEm(TablesRec.TableNum, 1, 1, 1, 1, 1, 1)
  728.   CALL ShowIt(TempRec, IndexName$, TheTable, Key1$)
  729.  
  730.   ' GetOperand lets user specify the way the SEEK is to be conducted ---
  731.   ' either  =, >, >=, <, or <= the value that was entered above
  732.  
  733.   DidIt = GetOperand%(Operand$)
  734.  
  735.   ' The actual SEEK has to be done according to two factors, the Index on
  736.   ' which it is conducted, and the condition chosen in GetOperand. In the
  737.   ' next section, case on the Operand returned, then IF and ELSEIF on the
  738.   ' basis of the index on which the search is being conducted
  739.  
  740.   IF Operand$ <> "<>" THEN                ' "<>" represents user ESC choice
  741.  
  742.    SELECT CASE Operand$
  743.     CASE "", "="                        ' If operand ="" or "=", use =
  744.       IF IndexName$ = "BigIndex" THEN
  745.         IF INSTR(Key2$, ",") = 0 THEN Key2$ = TransposeName(Key2$) ' a name
  746.         SEEKEQ TheTable, Key1$, Key2$, Key3#
  747.       ELSEIF IndexName$ = "NameIndexCH" OR IndexName$ = "AuthorIndexBS" THEN
  748.         IF INSTR(Key1$, ",") = 0 THEN Key1$ = TransposeName(Key1$) ' a name
  749.         SEEKEQ TheTable, LTRIM$(RTRIM$(Key1$))
  750.       ELSEIF IndexName$ = "IDIndex" THEN
  751.         SEEKEQ TheTable, NumberToSeek#
  752.       ELSEIF IndexName$ = "CardNumIndexCH" OR IndexName$ = "ZipIndexCH" THEN
  753.         SEEKEQ TheTable, NumberToSeek&
  754.       ELSE
  755.         SEEKEQ TheTable, Key1$
  756.       END IF
  757.     CASE ">="                      ' at least gets them close
  758.       IF IndexName$ = "BigIndex" THEN
  759.         IF INSTR(Key2$, ",") = 0 THEN Key2$ = TransposeName(Key2$) ' a name
  760.         SEEKGE TheTable, Key1$, Key2$, Key3#
  761.       ELSEIF IndexName$ = "NameIndexCH" OR IndexName$ = "AuthorIndexBS" THEN
  762.         IF INSTR(Key1$, ",") = 0 THEN Key1$ = TransposeName(Key1$)
  763.         SEEKGE TheTable, Key1$
  764.       ELSEIF IndexName$ = "IDIndex" THEN
  765.         SEEKGE TheTable, NumberToSeek#
  766.       ELSEIF IndexName$ = "CardNumIndexCH" OR IndexName$ = "ZipIndexCH" THEN
  767.         SEEKGE TheTable, NumberToSeek&
  768.       ELSE
  769.         SEEKGE TheTable, Key1$
  770.       END IF
  771.     CASE ">"
  772.       IF IndexName$ = "BigIndex" THEN
  773.         IF INSTR(Key2$, ",") = 0 THEN Key2$ = TransposeName(Key2$)
  774.         SEEKGT TheTable, Key1$, Key2$, Key3#
  775.       ELSEIF IndexName$ = "NameIndexCH" OR IndexName$ = "AuthorIndexBS" THEN
  776.         IF INSTR(Key1$, ",") = 0 THEN Key1$ = TransposeName(Key1$)
  777.         SEEKGT TheTable, Key1$
  778.       ELSEIF IndexName$ = "IDIndex" THEN
  779.         SEEKGT TheTable, NumberToSeek#
  780.       ELSEIF IndexName$ = "CardNumIndexCH" OR IndexName$ = "ZipIndexCH" THEN
  781.         SEEKGT TheTable, NumberToSeek&
  782.       ELSE
  783.         SEEKGT TheTable, Key1$
  784.       END IF
  785.     CASE "<="
  786.       IF IndexName$ = "BigIndex" THEN
  787.         IF INSTR(Key2$, ",") = 0 THEN Key2$ = TransposeName(Key2$)
  788.         SEEKGT TheTable, Key1$, Key2$, Key3#
  789.         MOVEPREVIOUS TheTable
  790.       ELSEIF IndexName$ = "NameIndexCH" OR IndexName$ = "AuthorIndexBS" THEN
  791.         IF INSTR(Key1$, ",") = 0 THEN Key1$ = TransposeName(Key1$)
  792.         SEEKGT TheTable, Key1$
  793.         MOVEPREVIOUS TheTable
  794.       ELSEIF IndexName$ = "IDIndex" THEN
  795.         SEEKGT TheTable, NumberToSeek#
  796.         MOVEPREVIOUS TheTable
  797.       ELSEIF IndexName$ = "CardNumIndexCH" OR IndexName$ = "ZipIndexCH" THEN
  798.         SEEKGT TheTable, NumberToSeek&
  799.         MOVEPREVIOUS TheTable
  800.       ELSE
  801.         SEEKGT TheTable, Key1$
  802.         MOVEPREVIOUS TheTable
  803.       END IF
  804.     CASE "<"
  805.       IF IndexName$ = "BigIndex" THEN
  806.         IF INSTR(Key2$, ",") = 0 THEN Key2$ = TransposeName(Key2$)
  807.         SEEKGE TheTable, Key1$, Key2$, Key3#
  808.         MOVEPREVIOUS TheTable
  809.       ELSEIF IndexName$ = "NameIndexCH" OR IndexName$ = "AuthorIndexBS" THEN
  810.         IF INSTR(Key1$, ",") = 0 THEN Key1$ = TransposeName(Key1$)
  811.         SEEKGE TheTable, Key1$
  812.         MOVEPREVIOUS TheTable
  813.       ELSEIF IndexName$ = "IDIndex" THEN
  814.         SEEKGE TheTable, NumberToSeek#
  815.         MOVEPREVIOUS TheTable
  816.       ELSEIF IndexName$ = "CardNumIndexCH" OR IndexName$ = "ZipIndexCH" THEN
  817.         SEEKGE TheTable, NumberToSeek&
  818.         MOVEPREVIOUS TheTable
  819.       ELSE
  820.         SEEKGE TheTable, Key1$
  821.         MOVEPREVIOUS TheTable
  822.       END IF
  823.     CASE ELSE
  824.       Alert$ = "The returned operand was " + Operand$
  825.       CALL ShowMessage(Alert$, 0)
  826.       SLEEP
  827.   END SELECT
  828.  ELSE                        ' If they choose ESC, go back to module level
  829.    CALL DrawScreen(TheTable)
  830.    CALL ShowRecord(TablesRec)
  831.    Alert$ = "You've escaped. " + KEYSMESSAGE
  832.    CALL ShowMessage(Alert$, 0)
  833.    SLEEP
  834.    Operand$ = ""
  835.  END IF
  836.   CALL EraseMessage
  837.   CALL DrawScreen(TheTable)
  838.   CALL Indexbox(TablesRec, CheckIndex%(TablesRec, FALSE))
  839.  IF EOF(TablesRec.TableNum) THEN
  840.   Alert$ = "Sorry,  unable to match value you entered with any field value"
  841.   CALL ShowMessage(Alert$, 0):  ' SLEEP: EraseMessage
  842.  END IF
  843.  
  844. EXIT SUB
  845.  
  846. SeekHandler:
  847.  IF ERR = 83 THEN                          ' 83 = Index not found
  848.     CALL DrawScreen(TablesRec.TableNum)
  849.     Alert$ = "SETINDEX for " + IndexName$ + " failed. Need more buffers?"
  850.     CALL ShowMessage(Alert$, 0)
  851.     EXIT SUB
  852.  END IF
  853.  
  854. END SUB   ' End of SeekRecord procedure
  855.