[comp.sys.handhelds] HP48SX: Full screen menu program, mark III

woodhams@phoenix.Princeton.EDU (Michael Woodhams) (01/31/91)

Sometime ago I wrote a program that would turn the top 5 rows of the
HP48SX keyboard into menu keys, and displayed the menu options on the
top of the screen. My use was to access frequently used astronomical
constants and units, so it acquired the name PHYCONST. An improved
version was posted by Tom Huber that had an improved user interface
for setting up the menu, and had a cleaner structure. I have now taken
his version and enhanced it again to produce mark III. I call this the
full screen menu program (or fsmenu) as it is by no means restricted
to physics or constants.

Enhancements over Tom Huber's version include:
  (1) Variable menu size - from one to eight rows can be turned into
a menu.
  (2) Multiple menus available - the programs (the menu modification
program and the program that actually runs the menu) take the names of
two variables as input, and these variables contain the menu
information.
  (3) The ability to define a key to run another full screen menu.
This allows multiple page menus, like the "next" key does for normal
menus.
  (4) When redefining a key, it is possible to leave the current label
unchanged by just pressing "enter" when prompted for the new label,
and to leave the value unchanged by pressing "N" for no change.
  (5) Error checking to ensure that key labels do not overflow their
boxes.
  (6) Error checking to gracefully abort if a key outside the menu
range is pressed (including shifted/alpha keys inside the key range)

Recommended use: Make a subdirectory called FSMENU somewhere, and put
KEYSIZE, UCLEAR and UMOD in it. Put UMENU in your home directory.
Using scrap paper, design your menu(s), deciding what you want to
assign to each key, and how to label them. Run UCLEAR to generate a
blank menu (it will prompt for how many rows you want the menu to
have) and store the top of stack object (a list of list of lists) in a
variable whose name suggests it contains menu values (I use UVL) and
the GROB in a variable whose name suggests it contains a picture of
the menu labels (I use UGR). Put the names of these variables on the
stack as algebraics, the graphics variable name in level 2 and the
values variable name in level 1. (e.g. 'UGR' enter 'UVL' enter) then
run UMOD. It will display the menu labels so far (all blank initially)
and wait for a keypress. Press the key you want to redefine. It will
then prompt for a label string. If you press enter, the label will be
left unchanged. If you type in a label which is too long, it will
return to prompting for a new label. Once a label has been entered, it
will give 6 options for what you want to do with this key. They are:
C: enter a constant. Selecting this will prompt for a number. If you
want to have units, append an underscore after the number and type in
the units. (You can use the units menus to speed this process.)
L: enter a library constant (if you have the Equation Library card.
This comes from Tom Huber's update of the program, and as I don't have
an EL card, I have never tested this part of the code.)
M: make this key shift you to a different menu. You will be prompted
to give the names of the variables containing the labels picture and
the values list for the other menu. I recommend redefining the "next"
key if you are going to do this.
N: No change. Useful if you only wanted to change the label of a key,
or pressed the wrong key to start off with.
U: Make a units key. This behaves just like an entry from the built in
units menus, including the blue and gold shift functions. Enter the
name of the unit (without an underscore).
After all this, the changes will be saved in the variables, and the
program will exit with the variable names still on the stack. To
redefine another key, just run UMOD again. To create another menu,
run UCLEAR again and save the blank menu variables under different
names (e.g. UVL2 and UGR2.)

I store the resulting variables in my home directory and redefine the
"CST" key to be \<< 'UGR' 'UVL' UMENU \>>. As the variables are in my
home directory, they are available from anywhere in the directory
hierachy. Furthermore, if (purely for the sake of argument) started
gambling on the stock exchange and had a directory subtree devoted to
such activities, by saving new 'UGR' and 'UVL' variables at the top of
that subtree, I could get a different menu anywhere within that
subtree, giving me interest calculations and conversions between Hong
Kong and New Zealand dollars, while everywhere else, the old menu
would still be available.

Disclaimers:
I do not yet have a cable, so the programs are entered by hand. Some
of the "\" codes may be wrong (especially \/= for "not equals") and I
may have mistyped something.

I have used only standard RPL functions, so if this program caused
your calculator to electrocute you, explode, start a nuclear war or
make analogies between naughts and crosses (tic-tac-toe to american
readers) and nuclear war, blame HP, not me.

If despite the above you still want to sue me, go ahead. I'm a
graduate student, and if you get everything I own, it still won't pay
your legal fees.

KEYSIZE:
Each inner list contains information for one row. The first number is
the number of keys in that row, the second is the x-coordinate of the
last pixel of the label for the first key in that row. (The first
pixel is assumed to be zero.) The third number is the x-coordinate of
the first pixel in the second key, and the fourth number is the
x-coordinate of the last pixel in the second key. The last number is
the offset for each key beyond the second. (All the remaining keys are
assumed to be the same size as the second.) There is one such list for
each of the first 8 rows.

{
  { 6 #20d #22d #42d #22d }
  { 6 #20d #22d #42d #22d }
  { 6 #20d #22d #42d #22d }
  { 6 #20d #22d #42d #22d }
  { 5 #42d #44d #64d #22d }
  { 5 #20d #24d #49d #27d }
  { 5 #20d #24d #49d #27d }
  { 5 #20d #24d #49d #27d }
}
checksum #45815d, 481 bytes


UCLEAR:
This program prompts for the number of rows of keys you want in your menu,
then leaves a GROB of a blank keyboard (with the correct number of keys) and
a list of lists of blank lists, which will be modified to contain the 
values/actions to be assigned to the keys. These should be stored in two
variables, e.g. UGR and UVL.
\<<
 "Enter number of rows"		@ Get number of rows of keys and
 { V } INPUT OBJ\->		@ store it in local variable "n"
 \-> n
  \<<
   IF 'n<1 OR n>8 OR FP(n)\/=0' THEN
    KILL			@ quit if n is out of range
   END
   ERASE {#0d #0d} PVIEW	@ erase screen and display it
   1 n FOR row			@ for each row
    KEYSIZE row GET		@ get the list of key size parameters
    LIST\-> DROP		@ disassemble it into components
    \-> m e1 s2 e2 inc      	@ and store in local variables
				@ m=number of keys in this row
				@ e1=last pixel in the first key
				@ s2=first pixel in the second key
				@ e2=last pixel in the second key
				@ inc=distance between start of second and
                                @     third keys
     \<<
      1 m FOR col		@ for each key in the row
       IF 'col==1' THEN	@ if first row, then
        #0d e1			@ draw from pixel 0 to e1
       ELSE			@ else
        col 2 - inc *
        DUP s2 +		@ draw from s2+(col-2)*inc
        SWAP e2 +		@ to e2+(col-2)*inc
       END
       \-> x1 x2		@ put the left and right x-coordinates of
				@ the key into x1 and x2
        \<<
         row 1 - 8 * DUP 6 + FOR line
				@ for line = (row-1)*8 to (row-1)*8+6
				@ (for line=top pixel of key to bottom pixel)
          x1 line R\->B 2 \->LIST @ make coordinate list {x1 line}
          x2 line R\->B 2 \->LIST @ make coordinate list {x2 line}
          LINE			@ draw line between coordinates
         NEXT			@ next line
        \>>			@ end of x1, x2 local variable block
      NEXT			@ next col (i.e. next key in the row)
     \>>			@ end of m, e1, s2, e2, inc variable block
   NEXT				@ next row
   PICT {#8d #41d} "\Ga"	@ write alpha on the alpha shift key
   1 \->GROB GXOR		@ (even if fewer than 6 rows were requested)
   PICT {#7d #49d}
   GROB 7 5 4020f72444 GXOR     @ write left arrow on gold shift key
   PICT {#7d #57d}
   GROB 7 5 0102f71211 GXOR	@ write right arrow on blue shift key
   PICT {#0d #0d}		@ put PICT, {#0d #0d}, {#131d n*#8-#1}
   #131d n #8d * #1d - 2 /->LIST @ onto stack
   SUB				@ and extract the picture of the keyboard
                                @   from the graphics screen. (If there were
				@   not enough rows for the alpha and/or shift
				@   keys to be displayed, the alpha, left and
				@   right arrows that were drawn on the screen
				@   will be left behind at this stage
   { }				@ Now make the list of key definitions
				@ These braces will delimit the list of
				@   row information
   1 n FOR row			@ for each row
    { }				@ These braces will delimit the list of
				@   information for each key in the row
    1 KEYSIZE row GET 1 GET	@ get number of keys in the row from KEYSIZE
     FOR col			@ for each key in the row
     { { } } +			@ add an empty list to the row list 
				@ for each key in the row
    NEXT			@ next col
    1 /->LIST			@ Imbed the row list in another set of braces
    +				@ add the row list to the master list
   NEXT				@ next row
  />>				@ end of n local variable block
/>>				@ end of UCLEAR program
Checksum: #32473d, 765.5 bytes

UMOD:
This program takes from the stack the names of the variables containing the
keyboard picture grob (in level 2) and the key values list (in level one.)
It leaves with the stack unaltered (i.e. the variable names are still there
to make it easier to run several times in a row.) The names should be entered
as algebraics, e.g. 'UGROB' ENTER 'UVALUES' UMOD.
\<<
 DUP RCL SIZE			@ find size (number of rows) of values variable
 \-> ugrob uvalues n		@ store names of variables in ugrob, uvalues
				@ and number of rows in n
  \<<
   ugrob RCL PICT STO		@ store keyboard picture in graphics screen
   {#0d #0d} PVIEW 		@ and display it
   0 WAIT			@ get a keystroke
   IF DUP n 1 + 10 * > THEN	@ if keystroke comes from a row after row n
    DROP ugrob uvalues KILL	@ restore stack and kill program
   END
   DUP 10 / IP			@ find row number of the key
   SWAP IP 10 MOD		@ and column number of the key
   \-> row col			@ store in local variables row, col
    \<<
     KEYSIZE row GET		@ get list for this row from KEYSIZE
     IF 'col==1' THEN		@ if this is the first key in the row
      2 GET #0d			@ the rightmost and leftmost pixels of the key
				@ are e1 (2nd element of list) and #0
     ELSE			@ if this is not the first key in the row
      LIST\-> DROP		@ disassemble the list
      col 2 - *			@ put inc*(col-2) on the stack
      SWAP OVER			@ stack contains:
				@ m e1 s2 inc*(col-2) e2 inc*(col-2)
      + ROT ROT +		@ stack contains:
				@ m e1 e2+inc*(col-2) s2+inc*(col-2)
      ROT DROP ROT DROP		@ stack contains:
				@ e2+inc*(col-2) s2+inc*(col-2)
				@ i.e. rightmost and leftmost pixels of the key
     END
     row 1 - 8 *		@ stack contains:
				@(rightmost pixel) (leftmost pixel) (top pixel)
     \-> x2 x1 y		@ store these in x2, x1, y
      \<<
       PICT			@ put PICT on stack for use later
       0			@ put a junk value on stack to drop inside loop
       DO
        DROP			@ drop junk value or previous bad label
	STD			@ put calc in std mode to display row and col
        row "," + col +
        " Label?" +		@ assemble prompt string
        { \Ga } INPUT		@ input label for key as a string
        1 \->GROB		@ turn it into a grob
       UNTIL
        DUP SIZE DROP		@ loop until x size of grob is smaller than key
        'x2-x1' EVAL \<=	@ ("\<=" means less than or equals. I may have
				@ the wrong code here.)
       END
       DUP SIZE DROP		@ get x size of grob on stack
       IF DUP #0d == THEN	@ if the label was a null string
        DROP DROP DROP		@ drop PICT, grob and grob size from stack,
				@ and leave keyboard picture unchanged
       ELSE
        y 'y+6' FOR line	@ for each row of pixels in the key
         x1 line R\->B 2 \->LIST @ make pixel coordinate {x1 line}
         x2 line R\->B 2 \->LIST @ make pixel coordinate {x2 line}
         LINE			@ draw a line between them
        NEXT			@ the key has now been erased
	'x1+x2+2' EVAL SWAP -	@ find x-coordinate to put grob at
        2 /			@ (x size of grob was already on stack)
	y #1d +			@ find y-coordinate to put grob at
	2 /->LIST SWAP GXOR	@ put label in keyboard picture
       END			@ end "if grob size = 0"
      />>			@ end of x2, x1, y local variable block
     PICT {#0d #0d} 
     #131d n #8d * 2 /->LIST	@ coordinates of lower right corner of picture
     SUB ugrob STO		@ extract picture from screen and store it
     uvalues RCL		@ get key values list
     DUP row GET		@ extract list for the row
     DO				@ loop until valid key input
      "C-constant object" 1 DISP @ display menu
      "L-library constant" 2 DISP
      "M-menu" 3 DISP
      "N-no change" 4 DISP
      "P-program name" 5 DISP
      "U-unit" 6 DISP
      0 WAIT IP			@ get (integer part of) key number
      CASE
       DUP 13 == THEN		@ "c" was pressed
        DROP			@ drop key number
        "Constant Object" { V }
        INPUT OBJ/->		@ input constant as string and make it object
        1 \->LIST		@ turn it into a one element list
        1			@ leave 1 on stack to indicate success
       END
       DUP 26 == THEN		@ if "l" pressed
        DROP			@ drop key number
        "Constant Name" 
        { \Ga } INPUT		@ input constant name as a string
        "\<< '" SWAP +          @ make string 
        "' CONST \>>" +		@ "\<< 'constantname' CONST \>>"
        OBJ\->			@ turn string into a program
        1 \->LIST		@ put program in a list
        1			@ indicate success
       END
       DUP 31 == THEN		@ if "m" pressed
        DROP			@ drop key number
        "\<< '"			@ start of program string
        "Graphic variable name" 
        { \Ga } INPUT + "' '" + @ get name of picture variable, add to string
        "Values variable name"
        { \Ga } INPUT +
        "' UMENU \>>" +		@ string contains
				@ "\<< 'grobname' 'valuename' UMENU \>>"
        OBJ\->			@ turn string into a program
        1 \->LIST		@ put program in a list
        1			@ indicate success
       END
       DUP 32 == THEN		@ if "n" pressed
        DROP			@ drop key number
        DUP col GET 1		@ get current value of key to stack
        1			@ indicate success
       END
       DUP 34 == THEN		@ if "p" pressed
        DROP			@ drop key number
        "Program name" { \Ga }
        INPUT "\<< "		@ get program name as a string
        SWAP + " \>>" + OBJ\->  @ make program \<< programname \>>
        1 ->LIST		@ put into a list
        1			@ indicate success
       END
        43 == THEN		@ if "u" pressed
        "Unit Name" { \Ga } INPUT @ get unit as a string
        \-> u
         \<<
          "\<< 1_" u + " * \>>"
          + OBJ\->		@ make program \<< 1_unit * \>>
          "\<< 1_" u + "CONVERT \>>"
          + OBJ\->		@ make program \<< 1_unit CONVERT \>>
          "\<< 1_" u + " / \>>"
          + OBJ\->		@ make program \<< 1_unit / \>>
          3 \->LIST		@ make a list of the three programs
         \>>
        1			@ indicate success
       END
       0			@ else indicate failure
      END			@ end of case statement
     UNTIL			@ loop again if 0 on stack
     END
     col SWAP PUT		@ put the object generated into row list
     row SWAP PUT		@ put the modified row list into main list
     uvalues STO		@ store in values variable
    />>				@ end of row, col local variable block
   ugrob uvalues		@ return variable names to stack
  />>				@ end of ugrob, uvalues, n local variable block
/>>				@ end of program
checksum #61103d bytes 1618.5

UMENU: This program takes the names of a grob and key values list from
the stack, displays the keyboard picture grob, waits for a keystroke,
and executes the appropriate code from the values list.

\<<
 \-> ugrob uvalues		@ store variable names in ugrob, uvalues
  \<<
   LCD\->			@ turn stack display into a picture
   {#0d #0d} ugrob RCL REPL	@ overwrite the top of picture with keyboard
   PICT STO			@ and store in graphics screen
   {#0d #0d} PVIEW		@ store composite keyboard/stack display
   0 WAIT			@ get keystroke
   IFERR			@ (keystroke might be from too low on keyboard)
    uvalues RCL 		@ put values list on stack
    OVER 10 / IP GET		@ extract row from this list (could give error)
    OVER 10 MOD GET		@ extract key from row list (should not give
				@ error)
   THEN				@ (does following if error)
    DROP DROP DROP KILL		@ return stack to pristine status and die
   END
   IFERR			@ (values list might not have defined response
				@ for a shifted key, or the key might never 
				@ have been defined by UMOD)
    SWAP FP 10 * GET 		@ extract program from key list
   THEN
    DROP DROP KILL		@ restore stack and die
   END
   EVAL				@ execute the program extracted from values
				@ list
  \>>				@ end of local variables list.
\>>
chksum: #17079d, 254 bytes.

HUBER@gacvx1.gac.edu (Tom Huber, Gustavus Adolphus College, (507) 933-7036) (02/01/91)

Michael Woodhams posted the program FSMENU, mark III
(Message-ID: <5816@idunno.Princeton.EDU>)

Michael has added several excellent features to the full screen menu
program.  There were, however, several typos in the posted programs.
Below are downloadable versions of the programs with the bugs (at least the
ones I found) fixed

(PS: I would recommend that you put KEYSIZE, UCLEAR and UMOD in your
HOME directory rather than in the FSMENU directory as recommended by
Michael otherwise it will be difficult to use UMOD from another directory)


	Tom Huber,   Physics Department 
	Gustavus Adolphus College,   St. Peter, MN  56082
	Bitnet: HUBER@GACVAX1   Internet: HUBER@GAC.EDU  (507)933-7036
	

Checksum: #B2F7h  Size:492.5  (Evaluated by 'KEYSIZE' BYTES)
KEYSIZE
%%HP: T(3)A(D)F(.);
{
  { 6 #20d #22d #42d #22d }
  { 6 #20d #22d #42d #22d }
  { 6 #20d #22d #42d #22d }
  { 6 #20d #22d #42d #22d }
  { 5 #42d #44d #64d #22d }
  { 5 #20d #24d #49d #27d }
  { 5 #20d #24d #49d #27d }
  { 5 #20d #24d #49d #27d }
}

Checksum: #7ED9h  Size:776
UCLEAR
%%HP: T(3)A(D)F(.);
\<<
 "Enter number of rows"		@ Get number of rows of keys and
 { V } INPUT OBJ\->		@ store it in local variable "n"
 \-> n
  \<<
   IF 'n<1 OR n>8 OR FP(n)\=/0' THEN
    KILL			@ quit if n is out of range
   END
   ERASE {#0d #0d} PVIEW	@ erase screen and display it
   1 n FOR row			@ for each row
    KEYSIZE row GET		@ get the list of key size parameters
    LIST\-> DROP		@ disassemble it into components
    \-> m e1 s2 e2 inc      	@ and store in local variables
				@ m=number of keys in this row
				@ e1=last pixel in the first key
				@ s2=first pixel in the second key
				@ e2=last pixel in the second key
				@ inc=distance between start of second and
                                @     third keys
     \<<
      1 m FOR col		@ for each key in the row
       IF 'col==1' THEN	@ if first row, then
        #0d e1			@ draw from pixel 0 to e1
       ELSE			@ else
        col 2 - inc *
        DUP s2 +		@ draw from s2+(col-2)*inc
        SWAP e2 +		@ to e2+(col-2)*inc
       END
       \-> x1 x2		@ put the left and right x-coordinates of
				@ the key into x1 and x2
        \<<
         row 1 - 8 * DUP 6 + FOR line
				@ for line = (row-1)*8 to (row-1)*8+6
				@ (for line=top pixel of key to bottom pixel)
          x1 line R\->B 2 \->LIST @ make coordinate list {x1 line}
          x2 line R\->B 2 \->LIST @ make coordinate list {x2 line}
          LINE			@ draw line between coordinates
         NEXT			@ next line
        \>>			@ end of x1, x2 local variable block
      NEXT			@ next col (i.e. next key in the row)
     \>>			@ end of m, e1, s2, e2, inc variable block
   NEXT				@ next row
   PICT {#8d #41d} "\Ga"	@ write alpha on the alpha shift key
   1 \->GROB GXOR		@ (even if fewer than 6 rows were requested)
   PICT {#7d #49d}
   GROB 7 5 4020F72444 GXOR     @ write left arrow on gold shift key
   PICT {#7d #57d}
   GROB 7 5 0102F71211 GXOR     @ write right arrow on blue shift key
   PICT {#0d #0d}		@ put PICT, {#0d #0d}, {#131d n*#8-#1}
   #131d n #8d * #1d - 2 \->LIST @ onto stack
   SUB				@ and extract the picture of the keyboard
                                @   from the graphics screen. (If there were
				@   not enough rows for the alpha and/or shift
				@   keys to be displayed, the alpha, left and
				@   right arrows that were drawn on the screen
				@   will be left behind at this stage
   { }				@ Now make the list of key definitions
				@ These braces will delimit the list of
				@   row information
   1 n FOR row			@ for each row
    { }				@ These braces will delimit the list of
				@   information for each key in the row
    1 KEYSIZE row GET 1 GET	@ get number of keys in the row from KEYSIZE
     FOR col			@ for each key in the row
     { { } } +			@ add an empty list to the row list 
				@ for each key in the row
    NEXT			@ next col
    1 \->LIST                   @ Imbed the row list in another set of braces
    +				@ add the row list to the master list
   NEXT				@ next row
  \>>                           @ end of n local variable block
\>>                             @ end of UCLEAR program

Checksum: #B2E9h  Size:1632
UMOD
%%HP: T(3)A(D)F(.);
\<<
 DUP RCL SIZE			@ find size (number of rows) of values variable
 \-> ugrob uvalues n		@ store names of variables in ugrob, uvalues
				@ and number of rows in n
  \<<
   ugrob RCL PICT STO		@ store keyboard picture in graphics screen
   {#0d #0d} PVIEW 		@ and display it
   0 WAIT			@ get a keystroke
   IF DUP n 1 + 10 * > THEN	@ if keystroke comes from a row after row n
    DROP ugrob uvalues KILL	@ restore stack and kill program
   END
   DUP 10 / IP			@ find row number of the key
   SWAP IP 10 MOD		@ and column number of the key
   \-> row col			@ store in local variables row, col
    \<<
     KEYSIZE row GET		@ get list for this row from KEYSIZE
     IF 'col==1' THEN		@ if this is the first key in the row
      2 GET #0d			@ the rightmost and leftmost pixels of the key
				@ are e1 (2nd element of list) and #0
     ELSE			@ if this is not the first key in the row
      LIST\-> DROP		@ disassemble the list
      col 2 - *			@ put inc*(col-2) on the stack
      SWAP OVER			@ stack contains:
				@ m e1 s2 inc*(col-2) e2 inc*(col-2)
      + ROT ROT +		@ stack contains:
				@ m e1 e2+inc*(col-2) s2+inc*(col-2)
      ROT DROP ROT DROP		@ stack contains:
				@ e2+inc*(col-2) s2+inc*(col-2)
				@ i.e. rightmost and leftmost pixels of the key
     END
     row 1 - 8 *		@ stack contains:
				@(rightmost pixel) (leftmost pixel) (top pixel)
     \-> x2 x1 y		@ store these in x2, x1, y
      \<<
       PICT			@ put PICT on stack for use later
       0			@ put a junk value on stack to drop inside loop
       DO
        DROP			@ drop junk value or previous bad label
	STD			@ put calc in std mode to display row and col
        row "," + col +
        " Label?" +		@ assemble prompt string
        { \Ga } INPUT		@ input label for key as a string
        1 \->GROB		@ turn it into a grob
       UNTIL
        DUP SIZE DROP		@ loop until x size of grob is smaller than key
        'x2-x1' EVAL \<=	@ ("\<=" means less than or equals. I may have
				@ the wrong code here.)
       END
       DUP SIZE DROP		@ get x size of grob on stack
       IF DUP #0d == THEN	@ if the label was a null string
        DROP DROP DROP		@ drop PICT, grob and grob size from stack,
				@ and leave keyboard picture unchanged
       ELSE
        y 'y+6' FOR line	@ for each row of pixels in the key
         x1 line R\->B 2 \->LIST @ make pixel coordinate {x1 line}
         x2 line R\->B 2 \->LIST @ make pixel coordinate {x2 line}
         LINE			@ draw a line between them
        NEXT			@ the key has now been erased
	'x1+x2+2' EVAL SWAP -	@ find x-coordinate to put grob at
        2 /			@ (x size of grob was already on stack)
	y #1d +			@ find y-coordinate to put grob at
        2 \->LIST SWAP GXOR     @ put label in keyboard picture
       END			@ end "if grob size = 0"
      \>>                       @ end of x2, x1, y local variable block
     PICT {#0d #0d} 
     #131d n #8d * 2 \->LIST    @ coordinates of lower right corner of picture
     SUB ugrob STO		@ extract picture from screen and store it
     uvalues RCL		@ get key values list
     DUP row GET		@ extract list for the row
     DO				@ loop until valid key input
      "C-constant object" 1 DISP @ display menu
      "L-library constant" 2 DISP
      "M-menu" 3 DISP
      "N-no change" 4 DISP
      "P-program name" 5 DISP
      "U-unit" 6 DISP
      0 WAIT IP			@ get (integer part of) key number
      CASE
       DUP 13 == THEN		@ "c" was pressed
        DROP			@ drop key number
        "Constant Object" { V }
        INPUT OBJ\->            @ input constant as string and make it object
        1 \->LIST		@ turn it into a one element list
        1			@ leave 1 on stack to indicate success
       END
       DUP 26 == THEN		@ if "l" pressed
        DROP			@ drop key number
        "Constant Name" 
        { \Ga } INPUT		@ input constant name as a string
        "\<< '" SWAP +          @ make string 
        "' CONST \>>" +		@ "\<< 'constantname' CONST \>>"
        OBJ\->			@ turn string into a program
        1 \->LIST		@ put program in a list
        1			@ indicate success
       END
       DUP 31 == THEN		@ if "m" pressed
        DROP			@ drop key number
        "\<< '"			@ start of program string
        "Graphic variable name" 
        { \Ga } INPUT + "' '" + @ get name of picture variable, add to string
        "Values variable name"
        { \Ga } INPUT +
        "' UMENU \>>" +		@ string contains
				@ "\<< 'grobname' 'valuename' UMENU \>>"
        OBJ\->			@ turn string into a program
        1 \->LIST		@ put program in a list
        1			@ indicate success
       END
       DUP 32 == THEN		@ if "n" pressed
        DROP			@ drop key number
        DUP col GET 1 \->LIST   @ get current value of key to stack
        1			@ indicate success
       END
       DUP 34 == THEN		@ if "p" pressed
        DROP			@ drop key number
        "Program name" { \Ga }
        INPUT "\<< "		@ get program name as a string
        SWAP + " \>>" + OBJ\->  @ make program \<< programname \>>
        1 \->LIST               @ put into a list
        1			@ indicate success
       END
        43 == THEN		@ if "u" pressed
        "Unit Name" { \Ga } INPUT @ get unit as a string
        \-> u
         \<<
          "\<< 1_" u + " * \>>"
          + OBJ\->		@ make program \<< 1_unit * \>>
          "\<< 1_" u + " CONVERT \>>"
          + OBJ\->		@ make program \<< 1_unit CONVERT \>>
          "\<< 1_" u + " / \>>"
          + OBJ\->		@ make program \<< 1_unit / \>>
          3 \->LIST		@ make a list of the three programs
         \>>
        1			@ indicate success
       END
       0			@ else indicate failure
      END			@ end of case statement
     UNTIL			@ loop again if 0 on stack
     END
     col SWAP PUT		@ put the object generated into row list
     row SWAP PUT		@ put the modified row list into main list
     uvalues STO		@ store in values variable
    \>>                         @ end of row, col local variable block
   ugrob uvalues		@ return variable names to stack
  \>>                           @ end of ugrob, uvalues, n local variable block
\>>                             @ end of program

Checksum: #42B7h  Size:263.5
UMENU
%%HP: T(3)A(D)F(.);
\<<
 \-> ugrob uvalues		@ store variable names in ugrob, uvalues
  \<<
   LCD\->			@ turn stack display into a picture
   {#0d #0d} ugrob RCL REPL	@ overwrite the top of picture with keyboard
   PICT STO			@ and store in graphics screen
   {#0d #0d} PVIEW		@ store composite keyboard/stack display
   0 WAIT			@ get keystroke
   IFERR			@ (keystroke might be from too low on keyboard)
    uvalues RCL 		@ put values list on stack
    OVER 10 / IP GET		@ extract row from this list (could give error)
    OVER 10 MOD GET		@ extract key from row list (should not give
				@ error)
   THEN				@ (does following if error)
    DROP DROP DROP KILL		@ return stack to pristine status and die
   END
   IFERR			@ (values list might not have defined response
				@ for a shifted key, or the key might never 
				@ have been defined by UMOD)
    SWAP FP 10 * GET 		@ extract program from key list
   THEN
    DROP DROP KILL		@ restore stack and die
   END
   EVAL				@ execute the program extracted from values
				@ list
  \>>				@ end of local variables list.
\>>