The following warnings occurred:
Warning [2] count(): Parameter must be an array or an object that implements Countable - Line: 795 - File: showthread.php PHP 7.4.33 (FreeBSD)
File Line Function
/showthread.php 795 errorHandler->error





Post Reply 
Custom Menus on High-End RPL Machines
04-02-2019, 12:28 AM (This post was last modified: 02-15-2020 01:56 AM by Giuseppe Donnini.)
Post: #1
Custom Menus on High-End RPL Machines
Given the recent interest in custom menus, it might be worthwhile to take a quick overview of the development, and continuous improvement, of the custom menu system on high-end RPL machines.



A. FIRST APPEARANCE : ORLANDO 1988 (HP-28S)

The HP-28S's immediate precursor Paladin (HP-28C, 1987) didn't yet provide custom menus, though not due to an oversight on behalf of the development team (headed by Bill Wickes), but because it was not deemed worth the effort given the exceedingly small amount of RAM available to the user (2K).

In fact, Bill Wickes was forced by HP managament to either wait a couple of years to build his symbolic "dream machine", or to do it on the pre-existing Champion hardware (HP-18C, 1986). Although the very first calculator to use the new RPL operating system (also developed by the same team), its OS was actually hidden under an algebraic user interface, and it had only 2K of RAM. This made sense for a business model, but would be severely limiting for an advanced scientific calculator of the kind envisioned by Bill Wickes. Nevertheless, he felt it was better than nothing, and accepted.

Despite being "a crippled product in many respects" (quoting Bill Wickes himself), the HP-28C became a huge success, particularly in the mathematical community, and was able to finance its successor, the HP-28S, which was actually a complete redesign of the HP-28C. With sixteen times more RAM available to the user (32K), it was now possible to implement things conceptualized long before, like user graphics, sub-directories, and custom menus.

Considering the complexity of the subject, the following discussion will limit itself to a particular aspect of the custom menu system, which best exhibits HP's continuous efforts to improve it, namely menu keys defined by global names. Given that restriction, the custom menu system as implemented in the HP-28S could be summarized as follows:


 OUTPUT CUSTOM MENUS (White on Black Labels)
+------------+--------------------------------------------------------------+
| ENTRY MODE | PRESSING [id] WITH OR W/O SHIFT:                             |
+------------+--------------------------------------------------------------+
| IMMEDIATE  | (If a command line is present, executes it, then) executes   |
|            | the contents of the global variable named id. (*)            |
|            |                                                              |
| ALGEBRAIC  | Echoes the string "id" (without quotes) to the command line. |
|            |                                                              |
| ALPHA      | Echoes the string "id" (without quotes) to the command line, |
|            | surrounded by spaces.                                        |
+------------+--------------------------------------------------------------+
 (*) If the global name references a directory (whose label in the HP-28S
     still lacks the distinguishing tab), its execution consists of making
     that directory the current one.

 INPUT CUSTOM MENUS (Black on White Labels)
+------------+--------------------------------------------------------------+
| ENTRY MODE | PRESSING [id] WITH OR W/O SHIFT:                             |
+------------+--------------------------------------------------------------+
| IMMEDIATE  | (If a command line is present, executes it, then) stores the |
|            | object on stack level 1 in the global variable named id. (*) |
|            |                                                              |
| ALGEBRAIC  | Echoes the string "id" (without quotes) to the command line. |
|            |                                                              |
| ALPHA      | Echoes the string "id" (without quotes) to the command line, |
|            | surrounded by spaces.                                        |
+------------+--------------------------------------------------------------+
 (*) If the global name references a directory, a "Directory Not Allowed"
     error is generated.


We may note that:

  1. For custom menus, the HP-28S doesn't distinguish between shifted and unshifted menu keys.
     
  2. The distinction between output and input custom menus is only significant in immediate entry mode.
     
  3. Input custom menus are similar to the Solver menu—their non-shifted keys input, rather than output, values, and both have, for that very reason, inverted key labels (black on white)—but they are not quite the same:
            
    • The Solver menu only operates on variable names directly or indirectly included in the current equation, whereas input custom menus may operate on any names.
            
    • In the Solver, shifted menu keys trigger (in immediate entry mode) the root finding process, whereas a custom input menu does not differentiate between shifted and unshifted menu keys.




B. MAJOR IMPROVEMENT : CHARLEMAGNE 1990 (HP-48SX)

With one of the major design goals of the HP-48SX being enhanced customizability, it comes as no surprise that the custom menu system was greatly improved, merging the strengths of both output and input types into a single, unified structure:


+------------+--------------------------------------------------------------+
| ENTRY MODE | PRESSING [id] W/O SHIFT:                                     |
+------------+--------------------------------------------------------------+
| IMMEDIATE  | (If a command line is present, executes it, then) executes   |
|            | the contents of the global variable named id. (*)            |
|            |                                                              |
| ALGEBRAIC  | Echoes the string "id" (without quotes) to the command line. |
|            |                                                              |
| PROGRAM    | Echoes the string "id" (without quotes) to the command line, |
|            | surrounded by spaces.                                        |
|            |                                                              |
| ALG + PRG  | --- same as in ALGEBRAIC mode ---                            |
+------------+--------------------------------------------------------------+
 (*) If the global name references a directory (now displayed with the
     distinguishing tab), its execution consists of making that directory
     the current one.

+------------+--------------------------------------------------------------+
| ENTRY MODE | PRESSING LEFT SHIFT + [id]:                                  |
+------------+--------------------------------------------------------------+
| IMMEDIATE  | (If a command line is present, executes it, then) stores the |
|            | object on stack level 1 in the global variable named id. (*) |
|            |                                                              |
| ALGEBRAIC  | --- same as in IMMEDIATE mode ---                            |
|            |                                                              |
| PROGRAM    | --- DEAD KEY (low beep) ---                                  |
|            |                                                              |
| ALG + PRG  | --- DEAD KEY (low beep) ---                                  |
+------------+--------------------------------------------------------------+
 (*) If the global name references a directory, a "Directory Not Allowed"
     error is generated.

+------------+--------------------------------------------------------------+
| ENTRY MODE | PRESSING RIGHT SHIFT + [id]:                                 |
+------------+--------------------------------------------------------------+
| IMMEDIATE  | (If a command line is present, executes it, then) recalls    |
|            | the contents of the global variable named id.                |
|            |                                                              |
| ALGEBRAIC  | --- same as in IMMEDIATE mode ---                            |
|            |                                                              |
| PROGRAM    | --- DEAD KEY (low beep) ---                                  |
|            |                                                              |
| ALG + PRG  | --- DEAD KEY (low beep) ---                                  |
+------------+--------------------------------------------------------------+




C. FURTHER REFINEMENT : HAMMER 1993 (HP-48GX)

In the HP-48GX this scheme was further enhanced by placing additional typing aids on the dead key positions of the HP-48SX:


+------------+--------------------------------------------------------------+
| ENTRY MODE | PRESSING [id] W/O SHIFT:                                     |
+------------+--------------------------------------------------------------+
| IMMEDIATE  | (If a command line is present, executes it, then) executes   |
|            | the contents of the global variable named id. (*)            |
|            |                                                              |
| ALGEBRAIC  | Echoes the string "id" (without quotes) to the command line. |
|            |                                                              |
| PROGRAM    | Echoes the string "id" (without quotes) to the command line, |
|            | surrounded by spaces.                                        |
|            |                                                              |
| ALG + PRG  | --- same as in ALGEBRAIC mode ---                            |
+------------+--------------------------------------------------------------+
 (*) If the global name references a directory, its execution consists of
     making that directory the current one.

+------------+--------------------------------------------------------------+
| ENTRY MODE | PRESSING LEFT SHIFT + [id]:                                  |
+------------+--------------------------------------------------------------+
| IMMEDIATE  | (If a command line is present, executes it, then) stores the |
|            | object on stack level 1 in the global variable named id. (*) |
|            |                                                              |
| ALGEBRAIC  | --- same as in IMMEDIATE mode ---                            |
|            |                                                              |
| PROGRAM    | Echoes the string "'id' STO" (without double quotes) to the  |
|            | command line, surrounded by spaces.                          |
|            |                                                              |
| ALG + PRG  | --- same as in PROGRAM mode ---                              |
+------------+--------------------------------------------------------------+
 (*) If the global name references a directory, a "Directory Not Allowed"
     error is generated.

+------------+--------------------------------------------------------------+
| ENTRY MODE | PRESSING RIGHT SHIFT + [id]:                                 |
+------------+--------------------------------------------------------------+
| IMMEDIATE  | (If a command line is present, executes it, then) recalls    |
|            | the contents of the global variable named id.                |
|            |                                                              |
| ALGEBRAIC  | --- same as in IMMEDIATE mode ---                            |
|            |                                                              |
| PROGRAM    | Echoes the string "'id' RCL" (without double quotes) to the  |
|            | command line, surrounded by spaces.                          |
|            |                                                              |
| ALG + PRG  | --- same as in PROGRAM mode ---                              |
+------------+--------------------------------------------------------------+




D. CONCLUSION

As is readily apparent from the above, the HP-48's custom menu system is vastly superior to that of the HP-28S, especially considering the fact that we only focussed on global names as menu key objects, leaving aside the two dozens other object types which may trigger their own menu key actions.

But what's missing in the 48 are 28S-sytle input menu keys, with their inverted labels, and their ability to store values with a single, unshifted keystroke—while at the same time remaining completely disconnected from the Solver environment.

Now, given the unrivalled flexibility of the 48, nothing prevents us from adding this feature ourselves! And while we're at it, we could even go a step further and combine the best of both worlds and have the new input keys retain their added, 48-specific functionality, albeit in a different distribution: unshifted keys would then store, left-shifted keys execute, and right-shifted keys recall the corresponding variable's contents. In short, we want 48-style variable keys, but with black on white labels and with their no- and left-shift actions interchanged. The skeleton of such a custom menu would look like this:


{
 {
  :: TakeOver $ "A" MakeInvLabel ;     ( *MENU KEY 1 Label Object* )
  {
   :: TakeOver ' ID A StdMenuKeyLS ;   ( *MENU KEY 1 No-Shift Action Object* )
   :: TakeOver ' ID A StdMenuKeyNS ;   ( *MENU KEY 1 Left-Shift Action Object* )
   :: TakeOver ' ID A StdMenuKeyRS ;   ( *MENU KEY 1 Right-Shift Action Object* )
  }
 }
 {
  :: TakeOver $ "B" MakeInvLabel ;     ( *MENU KEY 2 Label Object* )
  {
   :: TakeOver ' ID B StdMenuKeyLS ;   ( *MENU KEY 2 No-Shift Action Object* )
   :: TakeOver ' ID B StdMenuKeyNS ;   ( *MENU KEY 2 Left-Shift Action Object* )
   :: TakeOver ' ID B StdMenuKeyRS ;   ( *MENU KEY 2 Right-Shift Action Object* )
  }
 }

 ( ... )

}


( StdMenuKeyRS is an unsupported, but stable entry point at #4021Fh. )

This code works equally well in an HP-48SX and in an HP-48GX, but the newly added typing aids of the GX will obviously not be available on an SX (although they can be added).

In a thorough implementation, however, several things would have to be reconsidered:

  1. Replacing every single global name in a menu list with the convoluted code above would be very inefficient, and even if the process was automated, would still lead to a massive overhead both in size and time. The solution of choice is to hook the key label and key action builders themselves, i.e. the system programs responsible for building the actual key labels and key objects from the information contained in the menu definition.
     
  2. The custom menu list as presented above will interchange the unshifted and left-shifted key actions across all four entry modes, which might not be what we want. In fact, in algebraic entry mode, it would be quite confusing to have to press LS+[id] to simply copy the string "id" into the current expression. One might be tempted to just press [id], which, in its new STO function, would terminate the command line and store whatever the result of the latter's evaluation happens to be in the global variable id. It would therefore be wiser to follow the Solver menu's example and restrict the new functionality to immediate entry mode alone. This would also have the benefit of remaining consistent with the conventions of the system.
     
  3. It would be convenient to extend the new look and functionality to keys defined by local names, so that a program could use a custom input menu to prompt the user for values that will merely be stored in temporary variables.
     
  4. It would perhaps also be useful to make a distinction between global names referencing directories and all other global names, so that directories may preserve their familiar aspect and behavior. For local names, this distinction would not be necessary or even useful, since they can only reference unrooted directories (in contrast to directories rooted in user memory, which have the additional ability to become the current context when executed, and which are accessible only through global names).
     
  5. Another enhancement one might consider is getting visual confirmation in the status area after each storing operation—exactly as in the Solver environment, where you are greeted with a "ABC: 123" message once the real number 123 has been successfully stored in the global variable ABC.


With these improvements in mind, an input key in immediate entry mode would then have the following functionality (as usual, any pending command line is executed beforehand):


+-------+
|  NS   |
+-------+----------------------------------------------------------------------+
| [id]  | + Stores the object on stack level 1 in the global variable named id |
|       |   within the current directory.  If the variable doesn't exist,      |
|       |   creates it in the current directory.  If it already exists in the  |
|       |   current directory, its contents are overwritten with the new       |
|       |   object from stack level 1.                                         |
|       | + If the storing operation was successful, displays a confirmation   |
|       |   message in the status area of the form "id: contents".             |
|       | + If the object on stack level 1 is an unrooted directory, stores it |
|       |   in the global variable id within the current directory, thereby    |
|       |   rooting it in the latter as its sub-directory.  As a consequence,  |
|       |   the key label changes from black on white to white on black with a |
|       |   tab, and the key actions revert to their standard behavior (as     |
|       |   long as the new sub-directory remains in the current path).        |
|       | + The menu key offers the same protection as the STO key against     |
|       |   accidental overwriting of a variable (LASTARG returns the old,     |
|       |   rather than the new contents of the variable, so that the sequence |
|       |   RS+ARG STO restores the variable to its prior state).              |
|       | + If the object on stack level 1 is the same as id, generates a      |
|       |   "Circular Reference" error.                                        |
|       | + If there is no object on stack level 1, generates a "Too Few       |
|       |   Arguments" error.                                                  |
|       |                                                                      |
| [dir] | + [Standard white on black label with a tab]  Makes dir the current  |
|       |   context.  Unlike in the VAR menu, dir may be anywhere in the       |
|       |   current path, as is the case for standard custom menus, too.       |
|       |                                                                      |
|       |......................................................................|
|       |                                                                      |
| [lam] | + Stores the object on stack level 1 in the temporary variable named |
|       |   lam, overwriting the old contents.  If the temporary variable      |
|       |   doesn't exist, generates an "Undefined Local Name" error.          |
|       | + If the storing operation was successful, displays a confirmation   |
|       |   message in the status area of the form "lam: contents".            |
|       | + The menu key offers the same protection as the STO key against     |
|       |   accidental overwriting of a variable.                              |
|       | + If the object on stack level 1 is the same as lam, generates a     |
|       |   "Circular Reference" error.                                        |
|       | + If there is no object on stack level 1, generates a "Too Few       |
|       |   Arguments" error.                                                  |
+-------+----------------------------------------------------------------------+

+-------+
|  LS + |
+-------+----------------------------------------------------------------------+
| [id]  | + Executes the object stored in the global variable named id.  The   |
|       |   search for id is not restricted to the current directory, but is   |
|       |   extended to the current path.  If the variable is not found in the |
|       |   current path, it is considered as not yet defined (formal variable)|
|       |   and the global name 'id' is returned to the stack.                 |
|       |                                                                      |
| [dir] | + [Standard white on black label with a tab]  Generates a "Directory |
|       |   Not Allowed" error.  Unlike in the VAR menu, dir may be anywhere   |
|       |   in the current path.                                               |
|       |                                                                      |
|       |......................................................................|
|       |                                                                      |
| [lam] | + Recalls the object stored in the temporary variable named lam to   |
|       |   stack level 1.  If the temporary variable doesn't exist, generates |
|       |   an "Undefined Local Name" error.                                   |
+-------+----------------------------------------------------------------------+

+-------+
|  RS + | [N.B. All right-shifted menu keys retain their standard behavior.]
+-------+----------------------------------------------------------------------+
| [id]  | + Recalls the object stored in the global variable named id to       |
|       |   stack level 1.  The search for id is not restricted to the current |
|       |   directory, but is extended to the current path.  If the variable   |
|       |   is not found in the current path, generates an "Undefined Name"    |
|       |   error.                                                             |
|       |                                                                      |
| [dir] | + [Standard white on black label with a tab]  Recalls dir as an      |
|       |   unrooted data object to stack level 1.  Unlike in the VAR menu,    |
|       |   dir may be anywhere in the current path.                           |
|       |                                                                      |
|       |......................................................................|
|       |                                                                      |
| [lam] | + Recalls the object stored in the temporary variable named lam to   |
|       |   stack level 1.  If the temporary variable doesn't exist, generates |
|       |   an "Undefined Name" error.                                         |
+-------+----------------------------------------------------------------------+




E. PRACTICAL EPILOGUE

In a bold break with tradition, I will not leave the implementation of such a menu system as an exercise for the reader, but rather offer my own try at it. Further details may be gleaned from the commented source code. Observations, suggestions, and criticism are welcome. Gaudete!



[Image: YHFDKRE.png]     [Image: Hh7f2RF.png]     [Image: PEceIjq.png]
Ex. Temporary custom menu for easy vector input with mixed input and output keys.



Code:

*******************************************************************************
** TMN2                                                         SX/GX rev. A-R
*******************************************************************************
** ABSTRACT : Alternative TMENU (Temporary Menu).  Displays a user-defined menu
**            specified by a list or by the name of a (global or local)
**            variable that contains a list.
**
**            Differences to TMENU:
**            + TMN2 will treat keys defined by global or local names in a
**              different way:
**              - Their labels will be inverted (black on white instead of
**                white on black).
**              - In immediate entry mode, they will have their no- and left-
**                shift actions interchanged.
**            + TMN2 does not handle built-in and library menus (specified by
**              real numbers), because the result would be the same as with
**              TMENU.
**
**            Note that, if the argument provided is not a list or a name of a
**            variable containing a list, TMN2 will display an empty menu (as
**            does the GX version of TMENU), rather than generate an error (as
**            does the SX version).
**
** STACK    : ( {list.definition} --> )
**            ( 'name.definition' --> )
**
** ERRORS   : "Too Few Arguments" if no argument provided.
**
** DETAILS  : + The menu list provided by the user is incorporated into a full-
**              fledged System RPL menu of the form:
**
**              :: <properties> {data} ;
**
**              where {data} is the user-provided list, and <properties> the
**              place where the menu's general properties are defined,
**              including, among many others, the label builder definition and
**              the definition of the key handlers for each shift plane (this
**              part of a menu is normally inaccessible to the user).
**
**              The secondary is then passed as an argument to InitMenu, which
**              first resets all menu properties to their default state, then
**              executes its argument, which is what gives <properties> the
**              chance to overwrite any of the default properties.  After
**              execution terminates, the user list remains on the stack and is
**              then stored in internal RAM (to be precise, the reference of
**              the list already present in TEMPOB is removed from DSKTOP* and
**              written to MenuData in IRAM).
**
**            + The system will feed only the following 5 object types to the
**              label builder (be it the default one or a custom one):
**
**              1. Character String
**              2. Global Name
**              3. TakeOver Secondary (normal secos are decompiled!)
**              4. Graphics Object
**              5. System Binary Integer
**
**              Any other object type will first be decompiled to a character
**              string, before being passed to the label builder.  This means
**              that, if we want to get a chance to change the label of a key
**              defined by a local name, we have to check the key *action*
**              definition, rather than the key *label* definition.
*******************************************************************************

ASSEMBLE

* Binary transfer header ; remove if compiled directly on the HP-48.

        NIBASC /HPHP48-A/

* Unsupported, but stable entry points:

=NotIDorLAM?  EQU #2E7A4
=StdLabelDef  EQU #3A260

RPL

::
 CK1NoBlame        ( *Clear saved command name ; require 1 argument* )

 ::                (  ------------ BEGIN ARGUMENT TYPE CHECKING ------------- )
  DUPTYPELIST?     ( *If argument is a list, ...* )
  ?SEMI            ( *... quit type checking* )
  NotIDorLAM?      ( *If argument neither global nor local name, ...* )
  casedrop NULL{}  ( *... supply empty list instead* )
  SAFE@            ( *If global or local variable not defined, ...* )
  NOTcase NULL{}   ( *... supply empty list instead* )
  DUPTYPELIST?     ( *If contents of global or local var. not a list, ...* )
  NOTcasedrop
  NULL{}           ( *... supply empty list instead* )
 ;                 (  ------------- END ARGUMENT TYPE CHECKING -------------- )

*******************************************************************************
* If one prefers error messages (as done by the SX version of TMENU), replace
* the secondary above with the following one:
* ::
*  DUPTYPELIST? ?SEMI
*  NotIDorLAM? case SETYPEERR
*  SAFE@ NcaseSIZEERR
*  DUPTYPELIST? NcaseSIZEERR
* ;
*******************************************************************************

 '
 ::                ( xxxxxxxxxxxxx BEGIN CUSTOM MENU DEFINITION xxxxxxxxxxxxx )

                   ( ============= BEGIN CUSTOM MENU PROPERTIES ------------- )
  '
  ::               ( ------------ BEGIN CUSTOM KEY LABEL BUILDER ------------ )

                   ( --- Local Names --- )
   INDEX@ GETPROC  ( *Get key action definition [INDEX@ returns #keynumber]* )
   TYPELAM? case   ( *If lam, the system has already decompiled it to a $* )
   ::
    MakeInvLabel   ( *Build inverted, black on white key label* )
    Grob>Menu      ( *Display key label* )
   ;
                   ( --- General Case --- )
   DUPTYPEIDNT?    ( *If key label not defined by global name, ...* )
   NOTcase
   StdLabelDef     ( * ... use standard key label builder* )

                   ( --- Global Names --- )
   DUP ID>$        ( *Copy id and convert it to a string* )
   SWAP @          ( *Try to recall contents anywhere in the current path* )
   ITE
   TYPERRP?        ( *If variable defined, check for dir. ; signal T or F* )
   FALSE           ( *If variable undefined, signal F* )
   ITE
   MakeDirLabel    ( *If directory, build standard directory label with tab* )
   MakeInvLabel    ( *If not, build inverted, black on white label* )
   Grob>Menu       ( *Display key label* )

  ;                ( ------------- END CUSTOM KEY LABEL BUILDER ------------- )

  LabelDef!        ( *Store as new key label builder* )

  '
  ::               ( -------- BEGIN CUSTOM NO-SHIFT MENU KEY HANDLER -------- )

                   ( --- General Case --- )
   NotIDorLAM?     ( *If key not defined by global or local name, ...* )
   ImmedEntry? NOT ( *... or if we are not in immediate execution mode ...* )
   ORcase
   StdMenuKeyNS    ( *... use standard no-shift menu key handler* )

                   ( --- Directories --- )
   DUPTYPELAM?     ( *If key defined by local name, ...* )
   ?SKIP           ( *... skip distinction betw. dir. and non-dir. contents* )
   ::
    DUP@           ( *Copy id, try to recall contents anywhere in curr. path* )
    ITE
    TYPERRP?       ( *If variable defined, check for dir. ; signal T or F* )
    FALSE          ( *If variable undefined, signal F* )
    case
    ::             ( *If global variable contains a directory, ...* )
     2RDROP        ( *... cancel all pending jobs in our custom handler ...* )
     StdMenuKeyNS  ( *... and use standard no-shift menu key handler instead* )
    ;
   ;

                   ( --- Local, Global Formal and Global Non-Dir. Vars --- )
   StdMenuKeyLS    ( *Do standard left-shift key action* )

  ;                ( --------- END CUSTOM NO-SHIFT MENU KEY HANDLER --------- )

  MenuKeyNS!       ( *Store as new no-shift menu key handler* )

  '
  ::               ( ------- BEGIN CUSTOM LEFT-SHIFT MENU KEY HANDLER ------- )

                   ( --- General Case --- )
   NotIDorLAM?     ( *If key not defined by global or local name, ...* )
   ImmedEntry? NOT ( *... or if we are not in immediate execution mode ...* )
   ORcase
   StdMenuKeyLS    ( *... use standard left-shift menu key handler* )

                   ( --- Directories --- )
   DUPTYPELAM?     ( *If key defined by local name, ...* )
   ?SKIP           ( *... skip distinction betw. dir. and non-dir. contents* )
   ::
    DUP@           ( *Copy id, try to recall contents anywhere in curr. path* )
    ITE
    TYPERRP?       ( *If variable defined, check for dir. ; signal T or F* )
    FALSE          ( *If variable undefined, signal F* )
    case
    ::             ( *If global variable contains a directory, ...* )
     2RDROP        ( *... cancel all pending jobs in our custom handler ...* )
     StdMenuKeyLS  ( *... and use std. left-shift menu key handler instead* )
    ;
   ;

                   ( --- Local, Global Formal and Global Non-Dir. Vars --- )
   DoKeyOb         ( *Do standard no-shift key action* )

  ;                ( -------- END CUSTOM LEFT-SHIFT MENU KEY HANDLER -------- )

  MenuKeyLS!       ( *Store as new left-shift menu key handler* )

                   ( ============== END CUSTOM MENU PROPERTIES ============== )

 ;                 ( xxxxxxxxxxxxxx END CUSTOM MENU DEFINITION xxxxxxxxxxxxxx )

 SWAP >TCOMP       ( *Append user-supplied menu data to menu definition* )

 InitMenu          ( *Activate menu properties and display menu* )
;
Find all posts by this user
Quote this message in a reply
Post Reply 


Messages In This Thread
Custom Menus on High-End RPL Machines - Giuseppe Donnini - 04-02-2019 12:28 AM



User(s) browsing this thread: 3 Guest(s)