Stopping programs at mid-execution to let the user decide what to do is, in many cases, the only way to code your program. With the Prompt User (PMTUSR) command presented here, this activity doesnt have to imply creating dozens of display files and additional CL programs that will clutter your libraries.
You know how it is. Your users need an interactive program to display information from the sales database, but sometimes the database is not available for their use. This situation forces you to write additional code; for instance, youll need something to interrupt the program and ask the user what he wants to do if his request cannot be honored. Soon, your libraries are cluttered with little display files that can be used in one program onlyhardly the kind of scenario youd like when DASD is at a premium.
Not only that, but the CL program from which you need to prompt the user may already use a file of its own; in that case, you simply cannot use a second file for display purposes, because CL supports only one file. In such a case, youd have to create another CL program to drive the display file, adding complexity upon complexity. The Prompt User (PMTUSR) command presented here takes care of all these problems.
PMTUSR is a utility command that allows you to present ad hoc panels on the screen. You dont need to predefine anything, since you supply the text to be shown, the title of the panel, the instructions, and so oneven what input options are available and which function keys are considered valid. Figure 1 shows a sample of what PMTUSR can do.
To see how PMTUSR is implemented, take a look at a stripped-down version of the sales database inquiry program shown in Figure 2. As you can see, Ive coded a check for the database existence with a Check Object (CHKOBJ) command; if the check fails, the CL program gets escape message CPF9801, which triggers the PMTUSR command and the rest of the code within the DO/ENDDO block.
The PMTUSR command (all source code for programs and files described in this article can be found at www.midrange computing.com/mc/99/01) supplies everything via parameters:
TITLE provides the title at the top of the screenin high intensity and centered. You can supply up to 50 characters.
INSTR tells PMTUSR what kind of instructions to present. Special value *STD (standard) means Type option, press Enter if the prompt is input capable, or Please read, press Enter otherwise.
TEXT is for you to supply up to 12 lines of text, each with a maximum of 60 characters, to tell users whatever they need to know about the interruption.
INPUT decides whether to allow input (*YES), forbid it (*NO), or require it (*RQD).
CAPTION provides a caption for the input field. The caption can have a maximum of 50 characters.
VALUES lists the values the user can enter in the input field. You can supply up to 12 one-byte values; the special value *ANY means any value is valid.
RTNINPUT provides the input fields value as a return value in a CL variable that your CL program can use. So if the user types in an X in the input field and presses Enter, the variable named in RTNINPUT will have a value of X.
FKEYS decides which function keys are allowed. *STD enables function keys F3 and F12 so that users can press either one at their terminals; *F3 enables function key F3 only; *F12 enables function key F12 only; and *NONE disables both.
RTNFKEY provides a returned value of ENTER (if the user pressed Enter), F3, or F12, matching the function key the user pressed.
BEEP decides whether to sound the display stations alarm the first time the prompt is presented. If the user makes a mistake (pressing an invalid key or entering an invalid input value), PMTUSR wont sound the alarm a second time.
In Figure 2, youll see that I have coded the CL program so that it gives PMTUSR everything it needs to show the prompt panel illustrated in Figure 1. When PMTUSR ends and control comes back to the CL program, &RTNFKEY can tell you whether the user pressed F3, F12, or Enter, and &RTNINPUT can tell you which option the user selected. The CL program can then take appropriate action.
Its Workings, Revealed
The display file I use, USR007DF, has a few small tricks you should know about so that you can employ them in your own display files.
First, both the INPUT and NOINPUT records put information on the screen on lines that fall among those already written by the PROMPT record format. In fact, PROMPT writes to line 1 as well as 23, and yet INPUT needs to write to lines 19 and 20. Since I want to show both record formats together, I need to overlay the records, but DDSs OVERLAY keyword wont work in this case; I have to use CLRL(*NO), which instructs the workstation controller not to clear any lines when writing INPUT (or NOINPUT) on the screen.
Second, I wanted the cursor in a specific place on the screen when writing NOINPUT. I could have used the CSRLOC keyword, but theres a simpler methodcreating an input field and using DSPATR(PC) to position the cursor there. In addition, I included DSPATR(PR) so the input field is protected; the net effect is that the cursor is placed in an input field, but no input is allowed.
Third and last, I provided a message subfile. This technique requires two record formats, as all subfiles do. The data record (MSGRCD) needs the SFLMSGRCD keyword to indicate on which line the messages are to appear. It also needs SFLMSGKEY and SFLPGMQ, both of which require a field name but no definition (i.e., no specification for length or data type). The control record (MSGCTL), however, must use SFLDSP, SFLDSPCTL, SFLINZ, and SFLEND; it also requires SFLSIZ and SFLPAGthe way all subfiles doand a repeat of the SFLPGMQ keyword. Also, you must initialize the field attached to SFLPGMQ so it contains the name of your programUSR007CL, in this case.
The CL program, USR007CL, is PMTUSRs command processing program (CPP). I wont explain it all, but a few points are worth mentioning. At the beginning of USR007CL, I use a centering algorithm. I wanted the title, supplied to the command, to appear centered within the 50-byte output field on the display file. For example, if the title I supply consists of the word Error alone, thats five characters out of 50. Without a centered title, the prompt panel would look odd indeed.
To center the title, I resorted to a trick made possible by the command definition. The TITLE parameter is defined as having a maximum length of 50, but it also includes VARY(*YES *INT2) in the PARM statement; this makes the parameter of varying length, and the actual length is supplied in a 2-byte header, making the title 52 bytes long. When the CPP receives the TITLE parameter, it finds the actual length by extracting the first two bytes with the %BIN built-in function, assigning that value to decimal variable &TITLELEN.
Now, all thats left is to put some spaces before the title to make it look centered. I subtract the title length from 50 to find out how many unused positions are in the title and then divide that by 2, yielding &NBRBLANK, the number of blanks I need to place before the title. To compose the title then, I use &NBRBLANK bytes from a field named &BLANKS (50 bytes, containing blanks only) and then concatenate the title text, which begins at the third byte of the TITLE parameter value. Voilà!
One important aspect of using a message subfile is that, its your responsibility to clear it after each use. The easiest way to do so is to manually remove all messages from the current programs message queue, using the Remove Message (RMVMSG) command, immediately after each user input operation. This RMVMSG appears right below the Send Receive File (SNDRCVF) command, which is to CL what the Execute format (EXFMT) command is to RPG.
Yet More Tricks
PMTUSR is pretty tricky, but thats nothing compared with the tricks you can do with it. So far, I have shown you how to use it to interrupt a program and ask the user a simple question. But you can use it for other things, too. For example, you can use PMTUSR to display a menu, one thats built on the fly and processed immediately in the same CL program. You can even retrieve the options from a database file, making your menu rather dynamic. Of course, the limitation is that it display and recognize only 12 options (one per line, one per valid input value).
I feel that PMTUSR is quite useful, something youll want to have in your ever- fattening bag of tricks.
Reference AS/400 CL Programming (SC41-5721-01, CD-ROM QB3AUO01)
Figure 1: Typical output produced by PMTUSR
PGM
DCL VAR(&RTNINPUT) TYPE(*CHAR) LEN(1)
DCL VAR(&RTNFKEY) TYPE(*CHAR) LEN(5)
/* Check existence of sales database */
AGAIN:
CHKOBJ OBJ(SALESDB) OBJTYPE(*FILE)
MONMSG MSGID(CPF9801) EXEC(DO)
/* If not found, prompt user */
PMTUSR TITLE('Sales Database Not Available') +
INSTR(*STD) TEXT('The Sales Database is +
not available at this time.' 'Please do +
one of the following:' ' ' 'A. Alert +
QSYSOPR' 'T. Try again' 'C. Cancel') +
INPUT(*RQD) CAPTION('Enter your option +
here:') VALUES(A T C) RTNINPUT(&RTNINPUT) +
FKEYS(*STD) RTNFKEY(&RTNFKEY) BEEP(*YES)
/* Cancel request if F3 or F12 pressed */
IF COND(&RTNFKEY *EQ 'F3' *OR &RTNFKEY *EQ +
'F12') THEN(GOTO CMDLBL(CANCEL))
/* Enter pressed; check user input */
ELSE CMD(DO)
/* User selected to alert QSYSOPR */
IF COND(&RTNINPUT *EQ 'A') THEN(DO)
SNDMSG MSG('Sales Database not available') +
TOMSGQ(QSYSOPR)
GOTO CMDLBL(CANCEL)
ENDDO
/* User selected to try again */
ELSE CMD(IF COND(&RTNINPUT *EQ 'T') THEN(DO))
GOTO CMDLBL(AGAIN)
ENDDO
/* User selected to cancel */
ELSE CMD(DO)
GOTO CMDLBL(CANCEL)
ENDDO
ENDDO
ENDDO
/* Process sales database */
RETURN
/* Cancel sales database request */
CANCEL:
SNDPGMMSG MSGID(CPF9898) MSGF(QCPFMSG) MSGDTA('Sales +
database request not honored; database +
not available') MSGTYPE(*ESCAPE)
ENDPGM
Figure 2: This program illustrates use of the PMTUSR command
LATEST COMMENTS
MC Press Online