[Access to Wang masthead]

The NEWMENU Procedure

Making a procedure menu

From "VS Procedure",  Access 86, October 1986
  [ Prior Article ]     [ Return to the Catalog of articles ]     [ Next Article ]  

Ask twelve programmers how they create thier application menus and you're likely to get twelve different answers. With options ranging from pricey menu applications with complete security features to simple EZFORMAT menus, it's no wonder there are so many approaches.

I have used several approaches to menu creation. I have written programs in most of the major languages, used EZFORMAT, and even created menu systems. In spite of this experience, my personal development menu is a procedure.

Why write menus in Procedure language? Procedure menus are easily written and maintained. They can provide most error checking, and may screen users from unauthorized options. They make it easy to set or reset user defaults. Best of all, they can be used to satisfy GETPARM screens without having to create a myriad of special-purpose procedures.

On the negative side, procedure-created menus are slower and offer fewer formatting options. Unless properly trapped for error and cancel conditions, they can be cancelled along with the programs they control.

This month's procedure illustrates a general-purpose menu that allows up to 15 programs. It accepts the screen information at the top of the procedure, then builds the screen accordingly. The basic process is similar to Richard Evans's March 1985 article in On-Line Data Access ("Programmer Development Menu") except for the use of four new Procedure features: 512-character string variables, the new CALL statement, the &LABEL step label verification function, and error and cancel exit trapping for the RUN statements. (Due to the use of these features, this procedure requires Procedure interpreter 3.00.10. This is the version shipped with operating system 6.40 or later.)

How NEWMENU works

The procedure (see Figure 1) begins by creating a work table of text information to appear on the screen. This is accomplished by initializing a large string - 512 characters - with the items to be included. The text information is displayed within the PROMPT statement through a series of sub-string arguments to this large text string. The selected PF key is translated to the second half of a three-character string variable, &RUNSTEP; this will be used as our "soft" step label.

When the user presses a PF key, the integer value of that key becomes the last two positons of &RUNSTEP. How do we know if this newly-created step actually exists? A new Procedure built-in function - &LABEL - takes care of this. If the statement &LABEL(&RUNSTEP) is true (e.g. the value of &RUNSTEP exists as a step label) transfer passes to the CALL statement below. Otherwise, &ALARM is set and the user is asked for another selection.

If a valid selection is made, program control passes to the step label described by &RUNSTEP by invoking the CALL statement. CALL is similar to PERFORM in COBOL or GOSUB in BASIC; it transfers program control to the specified section, then returns to the point of origin after completing that section. Note that the end of a section must be indicated through an END statement.

In the example program, I have selected seven utilities to be run "straight" - e.g., without any procedural modifications to the default values. Notice step @6, however; it runs the REPORT utility but specifies several changes to the default values. Similarly, step @13 runs COPY, picking up the user-entered input file, library, and volume to act as the default values for the output file. These are small examples of the flexibility you gain in using Procedure to build menus.

Program control in most of the items is again assigned via CALL. This time, however, the file name is stored and a generalized RUN routine is called. Note the error trapping used in step SR; the CANCEL EXIT and ERROR EXIT allow the procedure to remain in control even if abnormal situations occur. To see how this works, key in a sample menu without these statements and run it. While in use, attempt to cancel the program (press HELP, then PF 16, and finally RETURN); the procedure will be cancelled in addition to the program.

When the selected program concludes, control is returned to the step label, then to the menu itself. The message field is updated to reflect the results of this processing. The user is now free to select the next item to be processed.

Using NEWMENU in your shop

Adapting NEWMENU to your needs amounts to changing the data in step S00, then creating the appropriate steps (@11, @2, etc.) in the body of the procedure. Once a shell of the menu is created, this task shouldn't prove to be difficult.

Reader Feedback

I received a few responses to my challenge to readers in the July issue ("ACCESSCK - User Security Profile") for a way to print a copy of the screen from a procedure. As you might recall, I wished to use the SCREEN subroutine in the Usersubs library to print the screen, but was not able to do so because the address of the screen was not known. Thanks to David A. Mizzell of Houston for submitting the correct solution.

The answer lies in the use of the PROCMSG function, a relatively new Procedure language feature. As demonstrated in Richard Evans's "VS Update" of August 1986, the PROCMSG function turns off the usual screen input and output except when explicitly called from the procedure through a PROMPT or MESSAGE statement. When specified at the top of the procedure, this option closes the screen during processing but leaves the screen contents intact. With the workstation thus closed, the SCREEN subroutine can be used.

Here are the steps required to add screen printing cabability to ACCESSCK:

1. Add PROCMSG = NO at the top of the program (figure two). This will close the screen between PROMPT or MESSAGE statements.

2. Add the data elements required by the SCREEN Usersub (figure three). Notice that the screen status is specified as 'C' (closed).

3. Add logic to accept the PF key (PF 14 in this case) and branch to the SCREEN subroutine (figure four).

4. Add the call to the SCREEN subroutine itself (figure five). Notice that the print file name is displayed to the user in the message field, and that the value of &ALARM is set to "YES"; this will cause the terminal to beep when the message is displayed.

Don't forget I am still interested in any problems you might have that might be solved with a procedure.

That's all for this month, and keep those suggestions coming. Next month I'll discuss some of the other extensions within release 3.00.10 of the Procedure interpreter, and propose some of my own.


Figure 1: The NEWMENU Procedure


     PROCEDURE NEWMENU

**********************************************************************
*
*                    NEWMWNU - Generic Menu Procedure
*
*    This procedure was featured in the October 1986 ACCESS 86.  It
*    allows users to build generic menus for any application.
*
*    Modify to your own needs by changing the descriptions in &MDESC
*    and then adding appropriate step labels corresponding to the
*    displayed item on the screen.  Any procedure statements may be
*    used within these steps.
*
*    SPECIAL NOTE: requires 3.00.10 of the Procedure interpreter.
*    (This release came with OP/SYS 6.40; run PROC and check the
*    number at the top of the screen.)
*
*    Written by Dennis S. Barnes
*
*    MODIFICATION HISTORY
*
*    Version 1.0   05/15/86   Initial version.
*
**********************************************************************

     DECLARE &RUNFIL     STRING (08)
     DECLARE &RUNLIB     STRING (08)
     DECLARE &RUNVOL     STRING (06)

     DECLARE &SYSVOL     STRING (06)

     DECLARE &MESSAGE    STRING (40)         INITIAL "Welcome, friend"
     DECLARE &ALARM      STRING (03)
     DECLARE &TITLE      STRING (21) INITIAL "Personal menu for user "
     DECLARE &RC         INTEGER

     DECLARE &MDESC      STRING (512)
     DECLARE &RUNSTEP    STRING (03)         INITIAL "@  "

     EXTRACT             &TITLE(19) = USERID,
                         &SYSVOL   = SYSVOL

S00: ASSIGN &MDESC [ 01 ]  =  "CONTROL  Control File maint.    "
                   [ 02 ]  !! "DATENTRY Data Entry utility     "
                   [ 03 ]  !! "INQUIRY  Inquire/Extract data   "
                   [ 04 ]  !! "REPORT   Report utility         "

                   [ 05 ]  !! "FILEDISP Find files on system   "
                   [ 06 ]  !! "TIMERPT  Run time report        "
                   [ 07 ]  !! "                                "
                   [ 08 ]  !! "DISPMANY Display files in lib.  "

                   [ 09 ]  !! "                                "
                   [ 10 ]  !! "                                "
                   [ 11 ]  !! "                                "
                   [ 12 ]  !! "                                "

                   [ 13 ]  !! "COPY     Copy a file or library "
                   [ 14 ]  !! "                                "
                   [ 15 ]  !! "DISPLAY  Display any file       "
                   [ 16 ]  !! "EXIT     Exit menu              "

S01: EXTRACT             &RUNLIB   = RUNLIB, &RUNVOL   = RUNVOL
     ASSIGN &ALARM       = "NO "

S02: PROMPT              PFKEY     = &RUNSTEP(2,2),
                         ALARM     = &ALARM
     CENTER BRIGHT LINE  &TITLE;;
     CENTER              "(01) ",   &MDESC((01*32)-31,32),
                         "    (09) ",  &MDESC((09*32)-31,32);
     CENTER              "(02) ",   &MDESC((02*32)-31,32),
                         "    (10) ",  &MDESC((10*32)-31,32);
     CENTER              "(03) ",   &MDESC((03*32)-31,32),
                         "    (11) ",  &MDESC((11*32)-31,32);
     CENTER              "(04) ",   &MDESC((04*32)-31,32),
                         "    (12) ",  &MDESC((12*32)-31,32);;
     CENTER              "(05) ",   &MDESC((05*32)-31,32),
                         "    (13) ",  &MDESC((13*32)-31,32);
     CENTER              "(06) ",   &MDESC((06*32)-31,32),
                         "    (14) ",  &MDESC((14*32)-31,32);
     CENTER              "(07) ",   &MDESC((07*32)-31,32),
                         "    (15) ",  &MDESC((15*32)-31,32);
     CENTER              "(08) ",   &MDESC((08*32)-31,32),
                         "    (16) ",  &MDESC((16*32)-31,32);;;
     CENTER BRIGHT       &MESSAGE;;

* No function assigned to key
     IF NOT &LABEL (&RUNSTEP)                GOTO SCRNERR

* Valid key pressed - perform option
     CALL &RUNSTEP
     IF &ALARM = "YES"   GOTO S02
     GOTO S01

* Invalid key pressed; display message

SCRNERR:
     ASSIGN &ALARM       = "YES"
     ASSIGN &MESSAGE     = "Invalid PFkey pressed; please try again"
     GOTO S02


@1:  ASSIGN &RUNFIL      = "CONTROL "
     CALL @RUN
     END

@2:  ASSIGN &RUNFIL      = "DATENTRY"
     CALL @RUN
     END

@3:  ASSIGN &RUNFIL      = "INQUIRY "
     CALL @RUN
     END

@4:  ASSIGN &RUNFIL      = "REPORT  "
     CALL @RUN
     END

@5:  ASSIGN &RUNFIL      = "FILEDISP"
     CALL @RUN
     END

@6:  RUN REPORT
     ENTER FUNCTION      04
     ENTER OPTIONS       ID        = "TIMERPT ",
                         OPTION    = "YES",
                         PAGE      = "77"
     ENTER FUNCTION      16
     END

@8:  ASSIGN &RUNFIL      = "DISPMANY"
     CALL @RUN
     END

@13: ASSIGN &RUNFIL      = "COPY    "

R13: RUN COPY IN @SYSTEM@
     ERROR EXIT          IS E13
     CANCEL EXIT         IS @CANC

F1:  DISPLAY INPUT
     DISPLAY OPTIONS
     DISPLAY OUTPUT      FILE      = (F1.FILE),
                         LIBRARY   = (F1.LIBRARY),
                         VOLUME    = (F1.VOLUME)
     ENTER EOJ           16

E13: ASSIGN &RC          = R13
     IF &RC NE 0         GOTO @ERR
     GOTO ENDRUN

@15: ASSIGN &RUNFIL      = "DISPLAY "
     CALL @RUN
     END

@16: RETURN

*             * ----- *  Subroutines follow * ----- *

@RUN: ASSIGN &ALARM      = "NO "
     IF EXISTS FILE      &RUNFIL IN &RUNLIB ON &RUNVOL GOTO SR
     IF EXISTS FILE      &RUNFIL IN @SYSTEM@ ON &SYSVOL GOTO SR

     ASSIGN &MESSAGE     = "Program " !! &RUNFIL !!
                         " not found - please try again"
     ASSIGN &ALARM       = "YES"
     END

SR:  RUN &RUNFIL IN &RUNLIB ON &RUNVOL
                         CANCEL EXIT         IS @CANC
                         ERROR EXIT          IS SRX

SRX: ASSIGN &RC          = SR
     IF &RC NE 0         GOTO @ERR

ENDRUN: ASSIGN &ALARM    = "NO "
     ASSIGN &MESSAGE     = "Program " !!
                         &RUNFIL !!
                         " successfully completed"
     END

@CANC: ASSIGN &MESSAGE     = "Program " !!
                         &RUNFIL !!
                         " cancelled by operator"
     ASSIGN &ALARM       = "YES"
     END

@ERR: ASSIGN &MESSAGE     = "Program " !!
                         &RUNFIL !!
                         " ended with error " !!
                         &RC
     ASSIGN &ALARM       = "YES"
     END

  [ Prior Article ]     [ Return to the Catalog of articles ]     [ Next Article ]  


Copyright © 1986 Dennis S. Barnes
Reprints of this article are permitted without notification if the source of the information is clearly identified