home *** CD-ROM | disk | FTP | other *** search
- '****************************** Main Module *******************************
- '* This window contains the module-level code of BOOKLOOK.BAS, a program *
- '* used to manage the database of a hypothethical library (BOOKS.MDB). The *
- '* program requires the following additional modules: BOOKMOD1.BAS, *
- '* BOOKMOD2.BAS, and BOOKMOD3.BAS, all named in the file BOOKLOOK.MAK. The *
- '* include file BOOKLOOK.BI and the database file BOOKS.MDB must also be *
- '* accessible. The program is discussed in Chapter 10, Database Programming*
- '* with ISAM in the BASIC Programmer's Guide. *
- '* *
- '* If you do NOT have expanded memory available, you should have invoked *
- '* the PROISAM.EXE TSR as PROISAM /Ib:n, where n can be between 10-20. *
- '* The /Ib: option specifies the number of buffers ISAM needs. Higher n *
- '* values improve performance. Too few buffers, and the program will fail *
- '* with an "Insufficient ISAM buffers" error. If /Ib: is set too high, *
- '* there may not be enough memory to load and run the program. If you have *
- '* expanded memory, ISAM automatically uses up to 1.2 megabytes, even if *
- '* you set Ib: to a low value. With a program the size of BOOKLOOK, use the*
- '* /Ie: option to reserve some expanded memory for QBX. This indirectly *
- '* limits the amount of expanded memory ISAM uses, but make sure ISAM gets *
- '* enough EMS for at least 15 buffers (each buffer = 2K). As a last resort,*
- '* you can start QBX with the /NOF switch to make more memory available. *
- '* *
- '* BOOKLOOK manages 3 tables, BookStock, CardHolders, and BooksOut. The *
- '* data in the BookStock and CardHolders tables is displayed as forms on *
- '* screen. The user can switch between table displays by pressing "V" (for *
- '* View Other Table). Each table is defined as a separate structure. The *
- '* structure for BookStock is Books, for CardHolders it is Borrowers, and *
- '* for BooksOut it is BookStatus. Each of these is incorporated as an *
- '* element of the structure RecStruct. RecStruct also has an element of *
- '* INTEGER type called TableNum (to keep track of which table is being *
- '* displayed), and a STRING element called WhichIndex that holds the name *
- '* of the index by which the user chooses to order presentation of records.*
- '* Press F2 to see a list of procedures called by the program. *
- '***************************************************************************
-
- DEFINT A-Z
- '$INCLUDE: 'BOOKLOOK.BI'
- SCREEN 0
- CLS ' TempRec is for editing and adding records
- DIM TempRec AS RecStruct ' Used only to blank out a TempRec
- DIM EmptyRec AS RecStruct ' See BOOKLOOK.BI for declaration of
- DIM BigRec AS RecStruct ' this structure and its elements
- DIM Marker(25) AS INTEGER ' Array to hold SAVEPOINT returns
-
- ' Open the database and the BookStock, CardHolders, and BooksOut tables
-
- ON ERROR GOTO MainHandler
- OPEN "BOOKS.MDB" FOR ISAM Books "BookStock" AS cBookStockTableNum
- OPEN "BOOKS.MDB" FOR ISAM Borrowers "CardHolders" AS cCardHoldersTableNum
- OPEN "BOOKS.MDB" FOR ISAM BookStatus "BooksOut" AS cBooksOutTableNum
- ON ERROR GOTO 0
-
- BigRec.TableNum = cBookStockTableNum ' Decide which table to show first
-
- ' Since the database has multiple tables, this outer DO loop is used to
- ' reset the number associated with the table the user wants to
- ' to access, then draw the screen appropriate to that table, etc.
- DO
- EraseMessage ' Show the interface
- CALL DrawScreen(BigRec.TableNum)
- Checked = CheckIndex%(BigRec, TRUE) ' Show current index
- CALL Retriever(BigRec, DimN, DimP, answer) ' Retrieve and show a record
- CALL ShowMessage(" Press V to View other table", 0)
- CALL ShowStatus(" Total records in table: ", CDBL(LOF(BigRec.TableNum)))
-
- ' This loop lets the user traverse BigRec.TableNum and insert, delete,
- ' or modify records.
- DO ' At start of each loop, show
- ' the user valid operations
- CALL Retriever(BigRec, DimN, DimP, answer) ' and display current record
-
- STACK 4000 ' Set large stack for recursions-it
- ' also resets FRE(-2) to stack 4000.
-
- answer% = GetInput%(BigRec) ' Find out what the user wants to do
-
- IF answer < UNDO THEN ' Excludes UNDOALL & INVALIDKEY too
- CALL EditCheck(PendingFlag, answer, BigRec)
- END IF
-
- SELECT CASE answer ' Process valid user requests
- CASE QUIT
- CALL ShowMessage(" You chose Quit. So long! ", 0)
- END
-
- ' If user picks "N" (Next Record), MOVENEXT.
- ' CheckPosition handles end-of-file (i.e. the
- CASE GOAHEAD, ENDK ' position just past the last record). If EOF
- ' or BOF = TRUE, CheckPosition holds position
- MOVENEXT BigRec.TableNum
- CALL CheckPosition(BigRec, answer, DimN, DimP)
-
- ' Same logic as GOAHEAD, but reversed
- CASE GOBACK, HOME
-
- MOVEPREVIOUS BigRec.TableNum
- CALL CheckPosition(BigRec, answer, DimN, DimP)
-
- ' If user chooses "E", let him edit a field.
- ' Assign the value returned by SAVEPOINT to
- ' an array element, then update the table and
- ' show the changed field. Trap any "duplicate
- CASE EDITRECORD ' value for unique index" (error 86) and
- ' handle it. The value returned by SAVEPOINT
- ' allows rollbacks so the user can undo edits
-
- IF LOF(BigRec.TableNum) THEN
- IF EditField(Argument%, BigRec, Letter$, EDITRECORD, answer%) THEN
-
- ' You save a sequence of savepoint identifiers in an array so
- ' you can let the user roll the state of the file back to a
- ' specific point. The returns from SAVEPOINT aren't guaranteed
- ' to be sequential.
- n = n + 1 ' Increment counter first so savepoint
- Marker(n) = SAVEPOINT ' is synced with array-element subscript
-
- Alert$ = "Setting Savepoint number " + STR$(Marker(n))
- CALL ShowMessage(Alert$, 0)
- ON ERROR GOTO MainHandler
- SELECT CASE BigRec.TableNum ' Update the table being displayed
- CASE cBookStockTableNum
- UPDATE BigRec.TableNum, BigRec.Inventory
- CASE cCardHoldersTableNum
- UPDATE BigRec.TableNum, BigRec.Lendee
- END SELECT
- ON ERROR GOTO 0
- ELSE
- COMMITTRANS ' Use COMMITTRANS abort transaction if
- PendingFlag = FALSE ' the user presses ESC
- n = 0 ' Reset array counter
- END IF
- ELSE
- CALL ShowMessage("Sorry, no records in this table to edit", 0): SLEEP
- END IF
- ' If choice is "A", get the values the user wants
- ' in each of the fields (with AddOne). If there
- ' is no ESCAPE from the edit, INSERT the record.
- ' Trap "Duplicate value for unique index" errors
- ' and handle them in MainHandler (error 86).
- CASE ADDRECORD
- added = AddOne(BigRec, EmptyRec, TempRec, answer%)
- IF added THEN
- Alert$ = "A new record assumes proper place in current index"
- CALL ShowMessage(Alert$, 0)
- ON ERROR GOTO MainHandler
- SELECT CASE BigRec.TableNum ' Insert into table being shown
- CASE cBookStockTableNum
- INSERT BigRec.TableNum, TempRec.Inventory
- CASE cCardHoldersTableNum
- INSERT BigRec.TableNum, TempRec.Lendee
- END SELECT
- ON ERROR GOTO 0
- END IF
- TempRec = EmptyRec
-
- ' If choice is "D" --- prompt for confirmation.
- ' If so, delete it and show new current record.
- CASE TOSSRECORD
- AnyRecords = LOF(BigRec.TableNum)
- IF BigRec.TableNum = cBookStockTableNum THEN CheckedOut = GetStatus(BigRec, 0#)
- IF BigRec.TableNum = cCardHoldersTableNum THEN
- SETINDEX cBooksOutTableNum, "CardNumIndexBO"
- SEEKEQ cBooksOutTableNum, BigRec.Lendee.CardNum
- IF NOT EOF(cBooksOutTableNum) THEN CheckedOut = TRUE
- END IF
- IF AnyRecords AND CheckedOut = FALSE THEN
- Alert$ = "Press D again to Delete this record, ESC to escape"
- CALL ShowMessage(Alert$, 0)
- DeleteIt% = GetInput%(BigRec)
- IF DeleteIt% = TOSSRECORD THEN ' Delete currently-displayed record
- DELETE BigRec.TableNum
- CALL ShowMessage("Record deleted...Press a key to continue", 0)
- ELSE
- CALL ShowMessage("Record not deleted. Press a key to continue", 0)
- CALL ShowRecord(BigRec)
- END IF
- ' The following code checks whether the record deleted was the last
- ' record in the index, then makes the new last record current
- IF EOF(BigRec.TableNum) THEN
- MOVELAST BigRec.TableNum
- END IF
- ELSE
- IF BigRec.TableNum = cBookStockTableNum THEN
- IF AnyRecords = 0 THEN Alert$ = "Sorry, no records in this table to delete"
- IF CheckedOut THEN Alert$ = "Can't delete --- this book currently checked out!"
- ELSEIF BigRec.TableNum = cCardHoldersTableNum THEN
- IF AnyRecords = 0 THEN Alert$ = "Sorry, no records in this table to delete"
- IF CheckedOut THEN Alert$ = "Can't delete --- this cardholder still has books out!"
- END IF
- CALL ShowMessage(Alert$, 0): SLEEP
- END IF
- CheckedOut = FALSE
-
- ' If user chooses "R", walk the fields so he
- ' can choose new index to order presentation
- CASE REORDER
- Letter$ = CHR$(TABKEY)
- GotOne = ChooseOrder(BigRec, EmptyRec, TempRec, Letter$, REORDER)
-
- ' If a choice of indexes was made, retrieve
- ' the index name, set an error trap, and try
- ' to set the index, then display new index.
- IF GotOne THEN
- IndexName$ = LTRIM$(RTRIM$(TempRec.WhichIndex))
- ON ERROR GOTO MainHandler
- IF IndexName$ <> "NULL" THEN ' This string is placed in
- SETINDEX BigRec.TableNum, IndexName$ ' TempRec.WhichIndex if
- ELSE ' user chooses "Default."
- SETINDEX BigRec.TableNum, "" ' "" is valid index name
- END IF 'representing NULL index
- ON ERROR GOTO 0 '(i.e. the default order)
- CALL AdjustIndex(BigRec)
- LSET TempRec = EmptyRec
- END IF
-
- ' If choice is "F", first set current index
- CASE SEEKFIELD ' using same procedure as REORDER. Then do seek.
-
- Letter$ = CHR$(TABKEY) ' Pass TABKEY for PlaceCursor
- GotOne = ChooseOrder(BigRec, EmptyRec, TempRec, Letter$, SEEKFIELD)
-
- IF GotOne AND TEXTCOMP(TempRec.WhichIndex, "NULL") THEN
- CALL SeekRecord(BigRec, TempRec, Letter$)
- FirstLetter$ = ""
- DimN = EOF(BigRec.TableNum): DimP = BOF(BigRec.TableNum)
- END IF
-
- ' STATUS gets the due date of a book & displays it
- CASE STATUS
- IF BigRec.TableNum = cBookStockTableNum THEN
- CALL ShowStatus("", 0#) ' Explicitly type the 0
- GotIt = GetStatus(BigRec, DateToShow#) ' to avoid type mismatch
- IF GotIt THEN
- Alert$ = "Press B for information on Borrower of this book"
- CALL ShowMessage(Alert$, 0)
- CALL ShowStatus("Due Date: ", DateToShow#)
- END IF
- END IF
-
- ' LendeeProfile displays borrower of displayed book
- CASE BORROWER
- CALL LendeeProfile(BigRec)
-
- ' BooksBorrowed shows books borrowed by CardHolder
- CASE WHICHBOOKS
- IF Borrowed THEN CALL BooksBorrowed(BigRec)
-
- ' If user hits "V" cycle through displayable tables
- CASE OTHERTABLE
- IF BigRec.TableNum < cDisplayedTables THEN
- BigRec.TableNum = BigRec.TableNum + 1
- ELSE
- BigRec.TableNum = 1
- END IF
- EXIT DO
- ' If user picks "I" to check current book back in,
- ' make sure it is out, then check it back in
- CASE CHECKIN
- IF Borrowed THEN
- GotIt = GetStatus(BigRec, DateToShow#)
- IF DateToShow# THEN
- CALL ReturnBook(BigRec, DateToShow#)
- END IF
- END IF
- ' If user picks "O" to check current book out,
- ' make sure it is available, then check it out
- CASE CHECKOUT
- GotIt = GetStatus(BigRec, DateToShow#)
- IF DateToShow# = 0# THEN
- CALL BorrowBook(BigRec)
- ELSE
- CALL ShowMessage("Sorry, this book is already checked out...", 0)
- END IF
-
- ' If user wants to Undo all or some of a series of
- ' uncommitted edits, make sure there is a pending
- ' transaction to undo, then restore the state of the
- ' file one step at a time, or altogether, depending
- ' on whether U or ^U was entered.
- CASE UNDO, UNDOALL
- IF PendingFlag = TRUE THEN
- IF n < 1 THEN
- CALL ShowMessage("No pending edits left to Undo...", 0)
- ELSE
- IF answer = UNDO THEN
- Alert$ = "Restoring back to Savepoint # " + STR$(Marker(n))
- CALL ShowMessage(Alert$, 0)
- ROLLBACK Marker(n)
- n = n - 1
- ELSE ' If it's not UNDO, it must be UNDOALL
- CALL ShowMessage("Undoing the whole last series of edits", 0)
- ROLLBACK ALL
- n = 0
- END IF
- END IF
- ELSE
- CALL ShowMessage("There are no pending edits left to Undo...", 0)
- END IF
-
- CASE INVALIDKEY ' Alert user if wrong key is pressed
- CALL ShowMessage(KEYSMESSAGE, 0)
- IF PendingFlag = TRUE THEN CALL DrawIndexBox(BigRec.TableNum, EDITRECORD)
- END SELECT
- CALL DrawHelpKeys(BigRec.TableNum)
- CALL ShowKeys(BigRec, BRIGHT + FOREGROUND, DimN, DimP)
- LOOP
- LOOP
- CLOSE
- END
-
- ' This error handler takes care of the most common ISAM errors
-
- MainHandler:
-
- IF ERR = 73 THEN ' 73 = Feature unavailable
- CALL ShowMessage("You forgot to load the ISAM TSR program", 0)
- END
- ELSEIF ERR = 88 THEN ' 88 = Database inconsistent
- ' If you have text files corresponding to each of the tables, then
- ' MakeOver prompts for their names and creates an ISAM file from them.
- CALL MakeOver(BigRec)
- RESUME NEXT
-
- ELSEIF ERR = 83 THEN ' 83 = Index not found
- CALL DrawScreen(BigRec.TableNum)
- CALL ShowMessage("Unable to set the index. Need more buffers?", 0)
- RESUME NEXT
- ELSEIF ERR = 86 THEN ' 86 = Duplicate value for unique index
- ' Trap errors when a user tries to enter a value for the Card Number or
- ' ID fields that duplicates a value already in the table
- IF answer = ADDRECORD THEN CALL DupeFixer(TempRec) ELSE CALL DupeFixer(BigRec)
- RESUME
- ELSE
- Alert$ = "Sorry, not able to handle this error in BOOKLOOK: " + STR$(ERR)
- CALL ShowMessage(Alert$, 0)
- END
- END IF
-
- '***************************************************************************
- '* The AddOne FUNCTION is called once for each field when the user wants *
- '* to add a record to the displayed table. *
- '* Parameters *
- '* BigRec RecStruct variable containing information on all tables *
- '* EmptyRec Empty record of same type as BigRec *
- '* TempRec Temporary record record of same type as BigRec *
- '* Answer Integer passed through to EditField; tells task to perform *
- '***************************************************************************
- FUNCTION AddOne (BigRec AS RecStruct, EmptyRec AS RecStruct, TempRec AS RecStruct, answer%)
- CALL ClearEm(BigRec.TableNum, 1, 1, 1, 1, 1, 1)
- LSET TempRec = EmptyRec
- CALL ShowMessage("Enter the first field of the new record", 0)
- TempRec.TableNum = BigRec.TableNum
- Edited = EditField(Argument%, TempRec, FirstLetter$, ADDRECORD, answer%)
- IF Edited THEN
- AddOne = -1
- ELSE
- AddOne = 0
- END IF
- COLOR FOREGROUND, BACKGROUND
- END FUNCTION
-
- '***************************************************************************
- '* The CheckPosition SUB checks the table position after the requested user*
- '* action is completed. If EOF follows a MOVENEXT or the user has chosen *
- '* MOVELAST, the Keys for Database Viewing/Editing box is updated to say *
- '* "No Next Record." If BOF follows a MOVEPREVIOUS or user has chosen a *
- '* MOVEFIRST, "No Previous Record" is displayed. *
- '* In either case, the position is held by executing MOVELAST or MOVEFIRST.*
- '* Parameters: *
- '* Big Rec User-defined type containing all table information *
- '* Answer Tells what operation retrieve results from *
- '* DimN & DimP Flags telling which menu items should be dimmed/changed *
- '***************************************************************************
- SUB CheckPosition (BigRec AS RecStruct, answer, DimN%, DimP%)
- SELECT CASE answer
- CASE GOAHEAD, ENDK
- IF EOF(BigRec.TableNum) OR (answer = ENDK) THEN
- CALL ShowMessage("This is the last record in this index", 0)
- DimN = TRUE: DimP = FALSE
- MOVELAST BigRec.TableNum
- ELSE ' If not EOF, turn on N
- DimN = FALSE: DimP = FALSE
- CALL EraseMessage
- END IF
- CASE GOBACK, HOME
- IF BOF(BigRec.TableNum) OR (answer = HOME) THEN
- CALL ShowMessage("This is the first record in this index", 0)
- DimP = TRUE: DimN = FALSE
- MOVEFIRST BigRec.TableNum
- ELSE
- DimP = FALSE: DimN = FALSE
- CALL EraseMessage
- END IF
- END SELECT
- END SUB
-
- '***************************************************************************
- '* The ChooseOrder FUNCTION calls PlaceCursor so the user can move around *
- '* the form to pick the index to set. *
- '* Parameters *
- '* BigRec BigRec has all the table information in updated form *
- '* EmptyRec EmptyRec is same template as BigRec, but fields are empty *
- '* TempRec Holds intermediate and temporary data *
- '* FirstLetter Catches letter if user starts typing during SEEKFIELD *
- '* Task Either REORDER or SEEKFIELD - passed on to PlaceCursor *
- '***************************************************************************
- FUNCTION ChooseOrder (BigRec AS RecStruct, EmptyRec AS RecStruct, TempRec AS RecStruct, FirstLetter$, Task%)
- CALL DrawTable(BigRec.TableNum)
- CALL DrawIndexBox(BigRec.TableNum, Task)
- Argument = TITLEFIELD ' Always start with first field
- TempRec = EmptyRec: TempRec.TableNum = BigRec.TableNum
-
- ' Pass temporary RecStruct variable so user can't trash BigRec
- value = PlaceCursor(Argument, TempRec, FirstLetter$, 1, Task)
-
- ' If the user chooses ESC, redraw everything, then exit to module level
- IF ASC(TempRec.WhichIndex) = 0 THEN
- CALL DrawIndexBox(BigRec.TableNum, Task)
- CALL ShowRecord(BigRec)
- CALL ShowMessage(KEYSMESSAGE, 0)
- ChooseOrder = 0
- EXIT FUNCTION
- ELSE ' Otherwise, if user makes a choice
- ChooseOrder = -1 ' of Indexes, signal success to the
- END IF ' module-level code
- END FUNCTION
-
- '***************************************************************************
- '* *
- '* The DupeFixer SUB is called when the tries to enter a duplicate value *
- '* for the BookStock table's IDnum column or the the CardHolders table's *
- '* CardNum column, because their indexes are Unique. The procedure prompts*
- '* the user to enter a new value. *
- '***************************************************************************
- SUB DupeFixer (BigRec AS RecStruct)
- IF BigRec.TableNum = cBookStockTableNum THEN
- DO
- Alert$ = STR$(BigRec.Inventory.IDnum) + " is not unique. "
- CALL ShowMessage(Alert$, 1)
- COLOR YELLOW + BRIGHT, BACKGROUND
- INPUT "Try another number: ", TempString$
- BigRec.Inventory.IDnum = VAL(TempString$)
- LOOP UNTIL BigRec.Inventory.IDnum
- ELSEIF BigRec.TableNum = cCardHoldersTableNum THEN
- DO
- Alert$ = STR$(BigRec.Lendee.CardNum) + " is not unique. "
- CALL ShowMessage(Alert$, 1)
- COLOR YELLOW + BRIGHT, BACKGROUND
- INPUT "Try another number: ", TempString$
- BigRec.Lendee.CardNum = VAL(TempString$)
- LOOP UNTIL BigRec.Lendee.CardNum
- END IF
- COLOR FOREGROUND, BACKGROUND
- END SUB
-
- '********************************* EditCheck SUB ***************************
- '* *
- '* The EditCheck procedure monitors what the user wants to do, and if the *
- '* choice is EDITRECORD, makes sure that a transaction is begun, or if it *
- '* already has begun, continues it. If a transaction has been pending, and *
- '* the user chooses anything except EDITRECORD, then the transaction is *
- '* committed. *
- '* *
- '* Parameters: *
- '* Pending A flag that indicates whether transaction is pending *
- '* Task Tells what operation the user wants to perform now *
- '* TablesRec Structure containing information about the tables *
- '* *
- '***************************************************************************
- SUB EditCheck (Pending, Task, TablesRec AS RecStruct)
- ' First, decide if this is a new or pending transaction, or not one at all
- ' The only transaction in this program keeps edits to the current record
- ' pending until the user moves on to a new record or a new operation
- ' (for example a Reorder).
- SHARED n ' n is index to array of savepoint ids
-
- IF Task = EDITRECORD THEN
- IF Pending = FALSE THEN
- BEGINTRANS
- Pending = TRUE
- END IF
- ELSEIF Pending = TRUE THEN ' Equivalent to Task<>EDITRECORD AND
- COMMITTRANS ' Pending=TRUE
- Pending = FALSE
- n = 0 ' Reset array index for savepoint ids
- CALL DrawIndexBox(TablesRec.TableNum, 0)
- END IF
- END SUB
-
- '***************************************************************************
- '* The GetInput FUNCTION takes the keystroke input by the user and returns*
- '* a constant indicating what the user wants to do. If the keystroke rep- *
- '* resents a valid operation, the choice is echoed to the screen. *
- '***************************************************************************
- FUNCTION GetInput% (BigRec AS RecStruct)
- DO
- answer$ = INKEY$
- LOOP WHILE answer$ = EMPTYSTRING
- IF LEN(answer$) > 1 THEN
- RightSide = HighKeys%(answer$)
- GetInput = RightSide
- ELSE
- SELECT CASE answer$
- CASE "A", "a"
- CALL UserChoice(BigRec, ALINE, 7, "Add Record")
- GetInput% = ADDRECORD
- CASE "B", "b"
- IF BigRec.TableNum = cBookStockTableNum THEN
- CALL UserChoice(BigRec, WLINE, 28, "Borrower")
- GetInput% = BORROWER
- ELSE
- CALL UserChoice(BigRec, WLINE, 13, "Books Outstanding")
- GetInput% = WHICHBOOKS
- END IF
- CASE "O", "o"
- CALL UserChoice(BigRec, CLINE, 7, "Check Book Out")
- GetInput% = CHECKOUT
- CASE "I", "i"
- CALL UserChoice(BigRec, CLINE, 28, "Check In")
- GetInput% = CHECKIN
- CASE "D", "d"
- CALL UserChoice(BigRec, ALINE, 28, "Drop Record")
- GetInput% = TOSSRECORD
- CASE "N", "n"
- GetInput% = GOAHEAD
- CASE "P", "p"
- GetInput% = GOBACK
- CASE "Q", "q"
- CALL UserChoice(BigRec, ELINE, 28, "Quit")
- GetInput% = QUIT
- CASE "E", "e"
- CALL UserChoice(BigRec, ELINE, 7, "Edit Record")
- GetInput% = EDITRECORD
- CASE "F", "f"
- CALL UserChoice(BigRec, RLINE, 28, "Find Record")
- GetInput% = SEEKFIELD
- CASE "R", "r"
- CALL UserChoice(BigRec, RLINE, 7, "Reorder Records")
- GetInput% = REORDER
- CASE "V", "v"
- GetInput% = OTHERTABLE
- CASE "W", "w"
- CALL UserChoice(BigRec, WLINE, 7, "When Due Back")
- GetInput% = STATUS
- CASE CHR$(ESCAPE)
- GetInput% = ESCAPE
- CASE "U", "u"
- GetInput = UNDO ' U signals rollback request after editing
- CASE CHR$(CTRLU) ' ^U = rollback a whole series of edits
- GetInput = UNDOALL
- CASE ELSE
- GetInput% = INVALIDKEY
- BEEP
- END SELECT
- END IF
- END FUNCTION
-
- '**************************************************************************
- '* The HighKeys FUNCTION handles common two-byte keys input by the user. *
- '* The Answer parameter is the keystroke entered by the user. * *
- '**************************************************************************
- FUNCTION HighKeys (answer AS STRING)
- SELECT CASE ASC(RIGHT$(answer$, 1)) ' Look at code for right byte
- CASE UP
- HighKeys = GOBACK ' UP is the up-arrow key
- CASE DOWN
- HighKeys = GOAHEAD ' DOWN is the down-arrow key
- CASE HOME
- HighKeys = HOME ' etc.
- CASE ENDK
- HighKeys = ENDK
- CASE LEFT
- HighKeys = OTHERTABLE
- CASE RIGHT
- HighKeys = OTHERTABLE
- CASE PGUP
- CALL ShowMessage("You could program so PGUP moves back n records", 0): SLEEP
- HighKeys = INVALIDKEY
- CASE PGDN
- CALL ShowMessage("You could program so PGDN moves forward n records", 0): SLEEP
- HighKeys = INVALIDKEY
- CASE ELSE
- CALL ShowMessage("Sorry, that key isn't handled yet.", 0): SLEEP
- HighKeys = INVALIDKEY
- END SELECT
- END FUNCTION
-
- '****************************** Retriever SUB ******************************
- '* The Retriever SUB retrieves records from the database file and puts *
- '* them into the appropriate recordvariable for the table being displayed. *
- '* An error trap is set in case the retrieve fails, in which case a message*
- '* is displayed. Note that if a preceding SEEKoperand fails, EOF is TRUE. *
- '* In that case, position is set to the last record, which is retrieved. *
- '* Parameters: *
- '* Big Rec User-defined type containing all table information *
- '* DimN & DimP Flags telling which menu items should be dimmed/changed *
- '* Task Tells what operation retrieve results from *
- '***************************************************************************
- SUB Retriever (BigRec AS RecStruct, DimN, DimP, Task)
- STATIC PeekFlag ' Set this if user is just peeking at other table
- LOCATE , , 0 ' Turn off the cursor
- ' Show the user which choice was made, and whether EOF or BOF
- CALL ShowKeys(BigRec, FOREGROUND + BRIGHT, DimN, DimP)
- ' If table is empty, don't try to retrieve anything
- IF LOF(BigRec.TableNum) = 0 THEN
- DrawTable (BigRec.TableNum)
- CALL ShowMessage("There are no records in this table", 0): EXIT SUB
- END IF
-
- IF Task <> ENDK AND Task <> HOME THEN
- IF Task < EDITRECORD THEN ' Edit needs its
- CALL Indexbox(BigRec, CheckIndex%(BigRec, 0)) ' own prompts. Show
- ELSEIF Task > INVALIDKEY THEN ' indexbox otherwise
- IF Task <> ESC THEN CALL DrawIndexBox(BigRec.TableNum, 0)
- CALL Indexbox(BigRec, CheckIndex%(BigRec, 0))
- END IF
- END IF
- IF BOF(BigRec.TableNum) THEN MOVEFIRST (BigRec.TableNum)
- ON LOCAL ERROR GOTO LocalHandler ' Trap errors on the retrieve.
- IF NOT EOF(BigRec.TableNum) THEN ' Retrieve current record
- SELECT CASE BigRec.TableNum ' from table being displayed
- CASE cBookStockTableNum ' if EOF is not true
- RETRIEVE BigRec.TableNum, BigRec.Inventory
- CASE cCardHoldersTableNum
- RETRIEVE BigRec.TableNum, BigRec.Lendee
- END SELECT
- ELSE ' If EOF is true, set position
- MOVELAST BigRec.TableNum ' to the last record in table,
- SELECT CASE BigRec.TableNum ' then retrieve the record
- CASE cBookStockTableNum
- RETRIEVE BigRec.TableNum, BigRec.Inventory
- CASE cCardHoldersTableNum
- RETRIEVE BigRec.TableNum, BigRec.Lendee
- END SELECT
- DimN = TRUE
- END IF
- ON LOCAL ERROR GOTO 0 ' Turn off error trap
- CALL ClearEm(BigRec.TableNum, 1, 1, 1, 1, 1, 1)
- CALL ShowRecord(BigRec)
- IF Task = OTHERTABLE THEN ' If user is just peeking at the other table
- IF PeekFlag = 0 THEN ' remind him how to get back to first table
- CALL ShowMessage("Press V to return to the other table", 0)
- PeekFlag = 1
- END IF
- ELSE
- PeekFlag = 0
- END IF
- EXIT SUB
-
- LocalHandler:
- IF ERR = 85 THEN
- CALL ShowMessage("Unable to retrieve your record...", 0)
- END IF
- RESUME NEXT
- END SUB
-
- '********************************* SeekRecord SUB *************************
- '* SeekRecord takes the name of the user's chosen index, sets it as the *
- '* current index, then prompts the user to enter the value to seek. A *
- '* minimal editor, MakeString, gets user input. If the SEEK is on a com- *
- '* bined index, GetKeyVals is called to get the input. Input is checked *
- '* for minimal acceptability by ValuesOK. If it is OK, GetOperand is *
- '* called to let the user specify how to conduct the SEEK. *
- '* Parameters: *
- '* TablesRec Contains current record information for all tables *
- '* TempRec Contains the name of the index on which to seek (in *
- '* TempRec.WhichIndex element) *
- '* Letter$ If the user starts typing instead of pressing ENTER *
- '* Letter$ catches the keystroke, passes it to MakeString *
- '**************************************************************************
- SUB SeekRecord (TablesRec AS RecStruct, TempRec AS RecStruct, Letter$)
- DIM EmptyRec AS RecStruct ' Make an empty record.
- IF LEFT$(Letter$, 1) < " " THEN ' Exit if value is not a valid
- ' character, then redraw
- CALL DrawIndexBox(TablesRec.TableNum, SEEKFIELD)
- CALL Indexbox(TablesRec, CheckIndex%(TablesRec, TRUE))
- CALL ShowMessage("You must enter a valid string or numeric value", 0)
- EXIT SUB
- END IF
- TheTable = TablesRec.TableNum
- IndexName$ = RTRIM$(TempRec.WhichIndex)
- IF GETINDEX$(TheTable) <> IndexName$ THEN ' If index to seek on is not
- ON LOCAL ERROR GOTO SeekHandler ' current, set it now. Trap
- SETINDEX TheTable, IndexName$ ' possible failure of SETINDEX
- ON LOCAL ERROR GOTO 0 ' then turn off error trap.
- END IF
- CALL AdjustIndex(TablesRec) ' Show the current index
- TablesRec.WhichIndex = TempRec.WhichIndex
- TempRec = EmptyRec ' Clear TempRec for data
- TempRec.TableNum = TablesRec.TableNum
- ' Get the value to SEEK for from the user. The data type you assign the
- ' input to must be the same as the data in the database, so get it as a
- ' string with MakeString, then convert it to proper type for index. If
- ' the index is the combined index BigIndex, use GetKeyVals for input...
-
- SELECT CASE RTRIM$(LTRIM$(IndexName$))
- CASE "TitleIndexBS", "AuthorIndexBS", "PubIndexBS", "NameIndexCH", "StateIndexCH"
- Prompt$ = "Value To Seek: "
- Key1$ = MakeString$(ASC(Letter$), Prompt$): IF Key1$ = "" THEN EXIT SUB
- CASE "IDIndex", "CardNumIndexCH", "ZipIndexCH"
- ValueToSeek$ = MakeString$(ASC(Letter$), Prompt$)
- IF ValueToSeek$ = "" THEN EXIT SUB
- IF IndexName$ = "IDIndex" THEN
- NumberToSeek# = VAL(ValueToSeek$)
- Key1$ = ValueToSeek$
- ELSE
- NumberToSeek& = VAL(ValueToSeek$)
- Key1$ = ValueToSeek$
- END IF
- CASE "BigIndex"
- CALL GetKeyVals(TempRec, Key1$, Key2$, Key3#, Letter$)
- ValueToSeek$ = STR$(Key3#)
- CASE ""
- Alert$ = "Sorry, can't search for field values on the default index"
- CALL ShowMessage(Alert$, 0)
- CASE ELSE
- END SELECT
-
- ' Make sure the input values are minimally acceptable
-
- IF NOT ValuesOK(TablesRec, Key1$, Key2$, ValueToSeek$) THEN
- CALL ShowMessage("Sorry, problem with your entry. Try again!", 0)
- EXIT SUB
- END IF
-
- ' Show the user the values he entered in their appropriate fields
- CALL ClearEm(TablesRec.TableNum, 1, 1, 1, 1, 1, 1)
- CALL ShowIt(TempRec, IndexName$, TheTable, Key1$)
-
- ' GetOperand lets user specify the way the SEEK is to be conducted ---
- ' either =, >, >=, <, or <= the value that was entered above
-
- DidIt = GetOperand%(Operand$)
-
- ' The actual SEEK has to be done according to two factors, the Index on
- ' which it is conducted, and the condition chosen in GetOperand. In the
- ' next section, case on the Operand returned, then IF and ELSEIF on the
- ' basis of the index on which the search is being conducted
-
- IF Operand$ <> "<>" THEN ' "<>" represents user ESC choice
-
- SELECT CASE Operand$
- CASE "", "=" ' If operand ="" or "=", use =
- IF IndexName$ = "BigIndex" THEN
- IF INSTR(Key2$, ",") = 0 THEN Key2$ = TransposeName(Key2$) ' a name
- SEEKEQ TheTable, Key1$, Key2$, Key3#
- ELSEIF IndexName$ = "NameIndexCH" OR IndexName$ = "AuthorIndexBS" THEN
- IF INSTR(Key1$, ",") = 0 THEN Key1$ = TransposeName(Key1$) ' a name
- SEEKEQ TheTable, LTRIM$(RTRIM$(Key1$))
- ELSEIF IndexName$ = "IDIndex" THEN
- SEEKEQ TheTable, NumberToSeek#
- ELSEIF IndexName$ = "CardNumIndexCH" OR IndexName$ = "ZipIndexCH" THEN
- SEEKEQ TheTable, NumberToSeek&
- ELSE
- SEEKEQ TheTable, Key1$
- END IF
- CASE ">=" ' at least gets them close
- IF IndexName$ = "BigIndex" THEN
- IF INSTR(Key2$, ",") = 0 THEN Key2$ = TransposeName(Key2$) ' a name
- SEEKGE TheTable, Key1$, Key2$, Key3#
- ELSEIF IndexName$ = "NameIndexCH" OR IndexName$ = "AuthorIndexBS" THEN
- IF INSTR(Key1$, ",") = 0 THEN Key1$ = TransposeName(Key1$)
- SEEKGE TheTable, Key1$
- ELSEIF IndexName$ = "IDIndex" THEN
- SEEKGE TheTable, NumberToSeek#
- ELSEIF IndexName$ = "CardNumIndexCH" OR IndexName$ = "ZipIndexCH" THEN
- SEEKGE TheTable, NumberToSeek&
- ELSE
- SEEKGE TheTable, Key1$
- END IF
- CASE ">"
- IF IndexName$ = "BigIndex" THEN
- IF INSTR(Key2$, ",") = 0 THEN Key2$ = TransposeName(Key2$)
- SEEKGT TheTable, Key1$, Key2$, Key3#
- ELSEIF IndexName$ = "NameIndexCH" OR IndexName$ = "AuthorIndexBS" THEN
- IF INSTR(Key1$, ",") = 0 THEN Key1$ = TransposeName(Key1$)
- SEEKGT TheTable, Key1$
- ELSEIF IndexName$ = "IDIndex" THEN
- SEEKGT TheTable, NumberToSeek#
- ELSEIF IndexName$ = "CardNumIndexCH" OR IndexName$ = "ZipIndexCH" THEN
- SEEKGT TheTable, NumberToSeek&
- ELSE
- SEEKGT TheTable, Key1$
- END IF
- CASE "<="
- IF IndexName$ = "BigIndex" THEN
- IF INSTR(Key2$, ",") = 0 THEN Key2$ = TransposeName(Key2$)
- SEEKGT TheTable, Key1$, Key2$, Key3#
- MOVEPREVIOUS TheTable
- ELSEIF IndexName$ = "NameIndexCH" OR IndexName$ = "AuthorIndexBS" THEN
- IF INSTR(Key1$, ",") = 0 THEN Key1$ = TransposeName(Key1$)
- SEEKGT TheTable, Key1$
- MOVEPREVIOUS TheTable
- ELSEIF IndexName$ = "IDIndex" THEN
- SEEKGT TheTable, NumberToSeek#
- MOVEPREVIOUS TheTable
- ELSEIF IndexName$ = "CardNumIndexCH" OR IndexName$ = "ZipIndexCH" THEN
- SEEKGT TheTable, NumberToSeek&
- MOVEPREVIOUS TheTable
- ELSE
- SEEKGT TheTable, Key1$
- MOVEPREVIOUS TheTable
- END IF
- CASE "<"
- IF IndexName$ = "BigIndex" THEN
- IF INSTR(Key2$, ",") = 0 THEN Key2$ = TransposeName(Key2$)
- SEEKGE TheTable, Key1$, Key2$, Key3#
- MOVEPREVIOUS TheTable
- ELSEIF IndexName$ = "NameIndexCH" OR IndexName$ = "AuthorIndexBS" THEN
- IF INSTR(Key1$, ",") = 0 THEN Key1$ = TransposeName(Key1$)
- SEEKGE TheTable, Key1$
- MOVEPREVIOUS TheTable
- ELSEIF IndexName$ = "IDIndex" THEN
- SEEKGE TheTable, NumberToSeek#
- MOVEPREVIOUS TheTable
- ELSEIF IndexName$ = "CardNumIndexCH" OR IndexName$ = "ZipIndexCH" THEN
- SEEKGE TheTable, NumberToSeek&
- MOVEPREVIOUS TheTable
- ELSE
- SEEKGE TheTable, Key1$
- MOVEPREVIOUS TheTable
- END IF
- CASE ELSE
- Alert$ = "The returned operand was " + Operand$
- CALL ShowMessage(Alert$, 0)
- SLEEP
- END SELECT
- ELSE ' If they choose ESC, go back to module level
- CALL DrawScreen(TheTable)
- CALL ShowRecord(TablesRec)
- Alert$ = "You've escaped. " + KEYSMESSAGE
- CALL ShowMessage(Alert$, 0)
- SLEEP
- Operand$ = ""
- END IF
- CALL EraseMessage
- CALL DrawScreen(TheTable)
- CALL Indexbox(TablesRec, CheckIndex%(TablesRec, FALSE))
- IF EOF(TablesRec.TableNum) THEN
- Alert$ = "Sorry, unable to match value you entered with any field value"
- CALL ShowMessage(Alert$, 0): ' SLEEP: EraseMessage
- END IF
-
- EXIT SUB
-
- SeekHandler:
- IF ERR = 83 THEN ' 83 = Index not found
- CALL DrawScreen(TablesRec.TableNum)
- Alert$ = "SETINDEX for " + IndexName$ + " failed. Need more buffers?"
- CALL ShowMessage(Alert$, 0)
- EXIT SUB
- END IF
-
- END SUB ' End of SeekRecord procedure
-