Tables are an easy way to organize data in ISPF. Here is an example of a REXX exec to collect employee data and save it in a table. The code is broken up into sections, to make it easier to talk about. You should be able to cut and paste the parts into an ISPF edit session to get a working ISPF table application.
/* REXX */ Address ISPEXEC "CONTROL ERRORS RETURN" "LIBDEF ISPTABL DATASET ID('xxxxxx.PRIVATE.TABLES')" "LIBDEF ISPTLIB DATASET ID('xxxxxx.PRIVATE.TABLES')" "LIBDEF ISPPLIB DATASET ID('xxxxxx.PRIVATE.PANELS')"
The initial /* REXX */ comment tells TSO that this is a REXX exec. The Address ISPEXEC statement tells TSO to let ISPF handle the lines in quotation marks ("). The first statement to be processed by ISPF is "control errors return". This tells ISPF that if any of the services fail, the application will handle the error, rather than having ISPF put up a severe error panel. The next 3 statements use the LIBDEF service to define which libraries are associated with which dialog elements. ISPTABL is the ddname for tables output library. Since this application will make changes to a table, we must allocate a data set to ISPTABL. ISPTLIB is the ddname for the input tables. ISPPLIB is the ddname for the panels.
"TBOPEN xyztable" If rc = 8 Then /* If table does not already exist, create it with one */ Do /* blank row */ "TBCREATE xyztable", "NAMES(XYZACTCD, XYZNAME, XYZJOB, XYZDEPT )" "TBVCLEAR xyztable" "TBADD xyztable" End
The next statement calls the TBOPEN service to open a table named XYZTABLE. The If statement checks the return code from the TBOPEN service. A return code of 8 from TBOPEN indicates that the table does not exist yet. If the table does not exist, the TBCREATE statement creates it. The table has 3 variables for each employee. XYZNAME holds the employee name, XYZJOB holds the employee's job title, and XYZDEPT holds the department number. XYZACTCD is an action code so that users can execute commands on a table row.
TBVCLEAR sets all the table variables to blank. The TBADD statement puts a new row into the table containing the current contents of all the variables in the table. These 2 statements together add a blank row to the table.
ztdsels = 0 ztdtop = 1 zcmd = "" cursor_loc = "CSRROW(1) CURSOR(XYZACTCD)"
Variable initialization comes next. Variables starting with letter Z are owned by ISPF. Don't give any application variables names that start with Z. ZTDSELS and ZTDTOP are special variables set by the TBDISPL service. ZTDSELS contains the number of table rows that have been selected. ZTDTOP is the number of the first row displayed on the table. ZCMD contains whatever was typed on the command line. Cursor_loc is an application variable to hold a string that tells ISPF where to place the cursor on the panel.
Do Forever "TBTOP xyztable" "TBSKIP xyztable NUMBER("ztdtop")" If ztdsels = 0 Then Do "TBDISPL xyztable PANEL(XYZPANEL) AUTOSEL(NO)" cursor_loc cursor_loc = "" End Else "TBDISPL xyztable"
The Do Forever statement displays the table in an infinite loop. Since TBTOP puts the Current Row Pointer (CRP) to before the first line of the table, the TBSKIP statement is needed to point to the first line of the table to be displayed. Variable ZTDSELS is set by the TBDISPL service to tell if there are rows that have selected but not yet processed. If no rows were selected, the TBDISPL service displays the table on panel XYZPANEL. If there are rows left to be processed, the TBDISPL service does not use the PANEL keyword to tell ISPF which panel to use, but rather displays the table using the same panel as the last display.
display_rc = rc xyzactcd = translate(strip(xyzactcd)) Select When xyzactcd = "" Then /* User typed over data on the line */ Do "TBPUT xyztable" "TBQUERY xyztable POSITION(CRPNAME)" cursor_loc = "CURSOR(XYZACTCD) CSRROW("crpname")" End When xyzactcd = "D" Then /* Delete the line */ Do xyzactcd = "" "TBDELETE xyztable" "TBQUERY xyztable POSITION(CRPNAME)" crpname = crpname + 1 cursor_loc = "CURSOR(XYZACTCD) CSRROW("crpname")" End When xyzactcd = "I" Then /* Insert a blank line */ Do xyzactcd = "" "TBPUT xyztable" "TBVCLEAR xyztable" "TBADD xyztable" "TBQUERY xyztable POSITION(CRPNAME)" cursor_loc = "CURSOR(XYZNAME) CSRROW("crpname")" End When xyzactcd = "P" Then /* Display a panel with more detail */ Do xyzactcd = "" "CONTROL DISPLAY SAVE" "DISPLAY PANEL(xyzpan2)" "TBPUT xyztable" "CONTROL DISPLAY RESTORE" "TBQUERY xyztable POSITION(CRPNAME)" cursor_loc = "CURSOR(XYZNAME) CSRROW("crpname")" End Otherwise "TBPUT xyztable" zerrsm = "Invalid action code" zerrlm = "Action code '"xyzactcd"' is not valid." zerralrm = "YES" "SETMSG MSG(ISRZ002)" "TBQUERY xyztable POSITION(CRPNAME)" cursor_loc = "CURSOR(XYZACTCD) CSRROW("crpname")" End ztdsels = ztdsels - 1
The TBDISPL service sets a return code of 0 if the enter key is pressed. If the end or return key is pressed, the TBDISPL service sets a return code of 8. If the TBDISPL succeeded, next we want to process any changes from the panel. The REXX select statement tells what action is to be taken for each of the valid line commands.
- If the row was overtyped, the TBPUT statement puts the changed row into the table.
- If the D line command was entered, the TBDELETE statement deletes that row of the table.
- If the I line command was entered, a blank line is added to the table.
- If the P line command was entered, the display environment is saved using the CONTROL statement. Panel XYZPAN2 is displayed to show additional detail. Any changes made on panel XYZPAN2 are put into the table with the TBPUT statement. Then the CONTROL statement restores the display environment for the next TBDISPL.
- If an invalid line command was entered, the message variables are set so that an error message can be displayed.
In all cases, the TBQUERY statement locates the row that has just been changed and sets the cursor location based on the latest change. Then we decrement ZTDSELS, since there is now one less line to process.
If ztdsels <= 0 Then Do zverb = translate(word(zcmd,1)) Select When display_rc = 8 /* END, RETURN, =X, etc. */, | zverb = "QUIT" Then Do If display_rc = 8 Then "TBCLOSE xyztable" Else "TBEND xyztable" Leave End When zverb = "SAVE" Then Do "TBSAVE xyztable" End When zverb = "" Then Nop Otherwise zerrsm = "Invalid command" zerrlm = "Action code '"zverb"' is not valid." zerralrm = "YES" "SETMSG MSG(ISRZ002)" cursor_loc = "CURSOR(ZCMD)" End End End
Next we want to check for commands on the command line. ISPF variable ZCMD contains the value of the command line. We set variable ZVERB to the first word on the command line.
- If End or Return is requested, ZVERB is set to QUIT. If ZVERB is QUIT, then if the END key was pressed on the panel, then we do a TBCLOSE to save the changes and close the table. If ZVERB is QUIT but the ENTER key was pressed, the TBEND service closes the table without saving the changes.
- If ZVERB is SAVE, the TBSAVE service saves the table.
- If nothing is entered on the command line nothing is done.
- Otherwise, the command was invalid, and the ISPF error message variables are set. Again, these variable names start with Z, and they have special meaning to ISPF. ZERRSM is the contents of the short message. ZERRLM is the contents of the long message. ZERRALRM tells whether to play a sound when the message displays. The SETMSG command displays the error message on the next panel.
)Attr Default(%+_) ! type( input) intens(high) caps(on ) just(left ) pad('''') ^ type( input) intens(low ) caps(off) just(asis ) pad('_') )Body Expand(//) %-/-/- Xyztable Display Panel -/-/- %Command ===>_zcmd / /%Scroll ===>_amt + % +Enter Action D (delete), I (insert), P (personnel details), + or overtype data to update row. + %Action Name Job Department + )Model clear(xyzactcd) !z + ^z + ^z + ^z + )Init .ZVARS = '(xyzactcd xyzname xyzjob xyzdept)' &amt = csr )End
- The )Attr section tells ISPF how each field on the panel is to be displayed.
- The )Body section is the part that shows up on the screen.
- The )Model section is what process the rows of the panel. The clear parameter resets the action code, variable XYZACTCD, to blanks. The line following the )MODEL statement is the model itself, and it shows the format for the table display.
- The )Init section handles panel initialization. The .ZVARS statement in the )Init section assigns any occurrence of the single character z to the variable names in the order listed. In this example, the model begins with variable XYZACTCD, and continues with variables XYZNAME, XYZJOB, and XYZDEPT. &AMT is the scroll amount, and it is set to CSR so that scrolling is based on cursor location.
- The )END section signifies the end of the panel definition.
We added a member called XYZPAN2 to the panels library to hold the definition for the employee information panel.
)ATTR DEFAULT(%+_) $ TYPE(INPUT) INTENS(HIGH) PAD(_) CAPS(OFF) )BODY %----------------------------- XYZ: Enter Data ------------------------- %COMMAND ===>_ZCMD % + Type name, job, and department: + + + Name %===>$xyzname + Job %===>$xyzjob + Department %===>$xyzdept + )END
This is a very simple example of how a table can be created, opened, updated, and displayed. There are lots of ways you can change this application to make it more robust. You can add the employee number to the table and make sure it is unique, or add a variable for a phone number or office location. You can add primary commands to sort the data or copy the data to a new table. You can add a line command to repeat a line, or you can add a digit to the I command and the D command to tell how many lines to insert or delete. You can verify data formats.Try playing around with it. Let us know how it goes in the comment section below.